Что такое OAuth2/OIDC и как интегрировать с Angular?angular-90

Что такое OAuth2 и OIDC?

OAuth2 - протокол авторизации, который позволяет приложениям получать ограниченный доступ к пользовательским данным без раскрытия паролей.

OpenID Connect (OIDC) - надстройка над OAuth2, добавляющая аутентификацию (подтверждение личности пользователя).

Основные различия:

  • OAuth2 = Авторизация (что можно делать)
  • OIDC = Аутентификация + Авторизация (кто ты + что можно делать)

Популярные потоки для SPA:

  1. Authorization Code Flow with PKCE (рекомендуемый для Angular)
  2. Implicit Flow (устаревший, не рекомендуется)

Пошаговая интеграция с Angular

1. Установка необходимых пакетов

npm install angular-oauth2-oidc

2. Настройка модуля аутентификации

import { AuthModule, OAuthModuleConfig } from 'angular-oauth2-oidc';

@NgModule({
  imports: [
    AuthModule.forRoot(),
    HttpClientModule
  ],
  providers: [
    {
      provide: OAuthModuleConfig,
      useValue: {
        resourceServer: {
          allowedUrls: ['https://api.example.com'],
          sendAccessToken: true
        }
      }
    }
  ]
})
export class AppModule {}

3. Конфигурация OAuthService

import { OAuthService } from 'angular-oauth2-oidc';

export class AuthService {
  constructor(private oauthService: OAuthService) {
    this.configureOAuth();
  }

  private configureOAuth() {
    this.oauthService.configure({
      issuer: 'https://auth.example.com',
      redirectUri: window.location.origin + '/callback',
      clientId: 'your-spa-client-id',
      responseType: 'code',
      scope: 'openid profile email',
      strictDiscoveryDocumentValidation: false,
      showDebugInformation: true // Только для разработки
    });

    this.oauthService.loadDiscoveryDocumentAndTryLogin();
  }
}

4. Реализация входа/выхода

login() {
  this.oauthService.initCodeFlow();
}

logout() {
  this.oauthService.logOut();
}

get isLoggedIn(): boolean {
  return this.oauthService.hasValidAccessToken();
}

get userName(): string {
  const claims = this.oauthService.getIdentityClaims();
  return claims?.['name'] || '';
}

5. HTTP Interceptor для автоматической подстановки токена

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
import { OAuthService } from 'angular-oauth2-oidc';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private oauthService: OAuthService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler) {
    if (this.oauthService.hasValidAccessToken()) {
      req = req.clone({
        setHeaders: {
          Authorization: `Bearer ${this.oauthService.getAccessToken()}`
        }
      });
    }
    return next.handle(req);
  }
}

6. Защита роутов с помощью Guard

import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private oauthService: OAuthService) {}

  canActivate(): boolean {
    if (this.oauthService.hasValidAccessToken()) {
      return true;
    }

    this.oauthService.initCodeFlow();
    return false;
  }
}

7. Обработка callback-роута

Добавьте в роутинг:

{
  path: 'callback',
  component: CallbackComponent
}
import { Component } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';

@Component({
  template: '<p>Processing login...</p>'
})
export class CallbackComponent {
  constructor(private oauthService: OAuthService) {
    this.oauthService.loadDiscoveryDocumentAndTryLogin().then(() => {
      if (this.oauthService.hasValidAccessToken()) {
        window.location.href = '/';
      }
    });
  }
}

Дополнительные настройки

Обновление токена

this.oauthService.setupAutomaticSilentRefresh();

Получение информации о пользователе

getUserProfile() {
  return this.oauthService.loadUserProfile();
}

Безопасность для SPA

  1. Всегда используйте Authorization Code Flow с PKCE
  2. Установите правильные CORS-настройки на сервере
  3. Используйте короткое время жизни access token (5-15 минут)
  4. Храните токены только в памяти, не в localStorage
  5. Используйте HttpOnly, Secure, SameSite cookies для refresh token

Пример конфигурации Keycloak

this.oauthService.configure({
  issuer: 'http://localhost:8080/auth/realms/your-realm',
  clientId: 'spa-client',
  dummyClientSecret: 'your-secret', // Для PKCE не нужно
  responseType: 'code',
  redirectUri: window.location.origin + '/callback',
  scope: 'openid profile email',
  requireHttps: false, // Только для разработки
  showDebugInformation: true
});

Резюмируем

Для интеграции OAuth2/OIDC с Angular используйте библиотеку angular-oauth2-oidc, реализуйте Authorization Code Flow с PKCE, настройте HTTP Interceptor и Route Guards. Всегда соблюдайте лучшие практики безопасности для SPA-приложений.