Проблемы с библиотекой OAuth2 и angular-auth2-oidc - поток кода PKCE

Я новичок в OAuth2 и библиотеке angular-auth2-oidc, так что несите меня если я сделаю некоторые ошибки новичка.

ЧТО Я ХОЧУ ДОСТИГНУТЬ: Пользователь щелкнет ссылку для входа на домашней странице, он будет отправлен на сайт провайдера для входа в систему, а затем перенаправит обратно на домашнюю страницу с токеном.

ТАКИЕ РЕЗУЛЬТАТЫ. Пользователь отправляется на сайт провайдера для входа в систему и успешно отправляется обратно на главную страницу моего сайта, но я не получаю токена. Я вывожу некоторые данные в консоль в ngOnit(), и все они имеют значение null, undefined или false.

Внутри функции ngOnit() я также выводю некоторые события, и события, которые я получаю в консоли, - это token_refresh_error. Я не обновляю токены в своем коде явно, но все равно получаю эту ошибку.

В подзаголовке Ошибка этого сообщения я добавил скриншоты с ошибками.

Вот моя конфигурация и реализация:

Вот конфигурация openid моего провайдера OAuth2:

authorization_endpoint                  "https://DOMAIN_PROVIDER/op/v1/auth"
code_challenge_methods_supported    
    0                                   "plain"
    1                                   "S256"
end_session_endpoint                    "https://DOMAIN_PROVIDER/op/v1/logout"
grant_types_supported   
    0                                    "authorization_code"
id_token_signing_alg_values_supported   
    0                                    "RS256"
issuer                                   "https://DOMAIN_PROVIDER/op"
jwks_uri                                 "https://DOMAIN_PROVIDER/op/v1/keys"
response_modes_supported    
    0                                    "query"
    1                                    "fragment"
    2                                    "form_post"
response_types_supported    
    0                                    "code"
scopes_supported    
    0                                    "openid"
subject_types_supported 
    0                                    "public"
token_endpoint                           "https://DOMAIN_PROVIDER/op/v1/token"
token_endpoint_auth_methods_supported   
    0                                    "client_secret_basic"
    1                                    "client_secret_post"
userinfo_endpoint                        "https://DOMAIN_PROVIDER/op/v1/userinfo"

Вот моя реализация:

// Компонент домашней страницы

export class HomeComponent implements OnInit {

  private oAuthConfig: AuthConfig;

  constructor(private oAuthService: OAuthService) { }

   ngOnInit() {

     // IN THIS METHOD I'M OUTPUTTING ALOT OF STUFF TO THE CONSOLE TO SEE IF IT IS WORKING

      if (!this.oAuthService.getAccessToken()) {
         this.initOAuthConfig();
         console.log('__________ACCESS-TOKEN IS NOT SET___________');
      } else {
         console.log('___ACCESS-TOKEN IS SET:', this.oAuthService.getAccessToken());
      }

      console.log('_____GRANTED-SCOPE:', this.oAuthService.getGrantedScopes());
      console.log('______IDENTITY-CLAIMS:', this.oAuthService.getIdentityClaims());
      console.log('______HAS-ACCESS-TOKEN?:', this.oAuthService.hasValidAccessToken());
      console.log('_____HAS-ACCESS-TOKEN?:', this.oAuthService.hasValidIdToken());
      
    // HERE I'M OUTPUTTNG THE EVENTS TO SEE WHAT IS GOING ON
    this.oAuthService.events.subscribe((e: OAuthErrorEvent) => {
      if (e.type === 'token_received') {
        this.logger.log('____________TOKEN RECEIVED');
      }
      this.logger.log('______====================EVENT-TYPE:', e.type);
      this.logger.log('______====================EVENT-REASON:', e.reason);
      this.logger.log('______====================EVENT-PARAMS:', e.params);
    });

  
    if (this.oAuthService.hasValidAccessToken()) {
      this.oAuthService.loadUserProfile().then((t) => {
        console.log('----USER-PROFILE:', t);
      });
    } else {
      this.logger.log('----HAS VALID ACCESS TOKEN');
    }

   }

  
   
   private initOAuthConfig(): void {
    this.oAuthConfig = {
      redirectUri: window.location.origin + '/login/oauth2/myapp',
      postLogoutRedirectUri: window.location.origin + '/logout',
      clientId: 'CLIENT-ID',
      scope: 'openid, rrn, profile',
      oidc: true,
      issuer: 'https://DOMAIN_PROVIDER/op',
      responseType: 'code',
      showDebugInformation: true,
      tokenEndpoint: 'DOMAIN_PROVIDER/op/v1/token',
      jwks: {'keys': ['DOMAIN_PROVIDER/op/v1/keys']}
    };

    this.oAuthService.setStorage(sessionStorage);
    this.oAuthService.configure(this.oAuthConfig);
    this.oAuthService.loadDiscoveryDocumentAndTryLogin();

}

// Класс, содержащий функцию входа

export class HeaderComponent {

    
   constructor(private oAuthService: OAuthService) { }


    public login() {
     this.oAuthService.initCodeFlow();
    }

    public logout() {
      this.oAuthService.logOut();
    }

}



// The html of the header component which contains the login link

    <a (click)="login()" title="Login">
       Login
    </a>

// Ошибки / Скриншоты ошибок

Ниже приведены ошибки в консоли, когда пользователя перенаправляют обратно на домашнюю страницу.

1) На этом снимке экрана значения равны null, false или undefined, кроме случаев, когда загружен документ обнаружения:

введите описание изображения здесь

2) На следующем снимке экрана вы увидите, что одним из ТИП СОБЫТИЯ является token_refresh_error. а ПРИЧИНА СОБЫТИЯ - BAD REQUEST. Он пытается сделать запрос к https//DOMAIN_PROVIDER/op/v1/token и не работает, хотя в методе de initOAuthConfig () я добавил атрибут tokenEndPoint. Вы также можете видеть, что это ошибка error: "invalid_grant". Во второй строке скриншота говорится: Ошибка при получении токена.

Я добавил jwks: {'keys': ['https://DOMAIN_PROVIDER/op/v1/keys']} в initOAuthConfig (), думая, что это решит проблему, но этого не произошло.

введите описание изображения здесь

// Вкладка приложения консоли: хранилище сеанса

На вкладке приложения консоли я вижу, что в хранилище сеанса есть несколько пар данных ключ-значение (в локальном хранилище данных нет). См. Следующий снимок экрана:

введите описание изображения здесь


person Eddy Freeman    schedule 14.10.2020    source источник


Ответы (1)


Я бы хотел реализовать ваше решение в 2 этапа:

  • Сначала разберитесь с шаблонами проектирования OAuth и Open Id Connect - надеюсь, я смогу помочь с этим с помощью простого примера кода TypeScript.
  • Подумайте, как лучше всего перевести образец на Angular, что, я уверен, вы сможете сделать достаточно легко.

КОНФИГУРАЦИОННЫЙ ФАЙЛ

Мне нравится выражать настройки OAuth через конфигурацию JSON - см. этот файл. Вы можете обновить файл в соответствии с вашей конфигурацией, запустить мой образец, и, надеюсь, вы увидите возвращающийся токен.

ОТВЕТСТВЕННОСТЬ КЛИЕНТА API

Когда пользовательский интерфейс вызывает API, он должен сначала получить токен доступа, а пользовательский интерфейс также должен обрабатывать просроченные / 401 ответы - см. этот класс.

ОТВЕТСТВЕННОСТЬ КЛИЕНТА OAUTH

Клиент API вызывает этот класс, чтобы получить токен, который использует клиентскую библиотеку OIDC для управления перенаправлениями, ответами и возвращаемыми токенами.

ПОЯСНИТЕЛЬНАЯ СТАТЬЯ В БЛОГЕ

См. Также эти статьи, которые я стремился сделать наглядными и удобными для чтения:

person Gary Archer    schedule 15.10.2020