#javascript #angular #oauth-2.0 #angular-oauth2-oidc
#javascript #angular #oauth-2.0 #angular-oauth2-oidc
Вопрос:
Я новичок в OAuth2 и библиотеке angular-auth2-oidc, поэтому потерпите, если я допущу какие-то ошибки новичка.
ЧЕГО Я ХОЧУ ДОБИТЬСЯ: пользователь нажмет на ссылку для входа на домашней странице, он будет отправлен на сайт провайдера для входа в систему, а затем перенаправит обратно на домашнюю страницу с помощью токена.
РЕЗУЛЬТАТЫ НА ДАННЫЙ МОМЕНТ: пользователь отправляется на сайт провайдера для входа в систему и успешно отправляется обратно на домашнюю страницу моего сайта, но я не получаю никакого токена. Я вывожу некоторые данные в консоль ngOnit()
, и все они являются нулевыми, неопределенными или ложными.
Внутри 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) На этом скриншоте значения равны нулю, false или не определены, за исключением случаев, когда загружен документ обнаружения:
2) На следующем скриншоте вы увидите, что один из ТИПОВ СОБЫТИЙ token_refresh_error
. и ПРИЧИНА СОБЫТИЯ BAD REQUEST
. Он пытается отправить запрос https//DOMAIN_PROVIDER/op/v1/token
и терпит неудачу, хотя в методе initOAuthConfig() я добавил tokenEndPoint
атрибут. Вы также можете видеть, что ошибка error: "invalid_grant"
. Во второй строке скриншота говорится об ошибке получения токена
Я добавил jwks: {'keys': ['https://DOMAIN_PROVIDER/op/v1/keys']}
initOAuthConfig(), думая, что это решит проблему, но это не так.
// Вкладка приложения консоли: хранилище сеансов
На вкладке приложения консоли я вижу, что в сеансовом хранилище есть несколько пар ключ-значение данных (в локальном хранилище данных нет) См. Следующий снимок экрана:
Ответ №1:
Я бы стремился реализовать ваше решение в 2 этапа:
- Сначала разберитесь с шаблонами проектирования OAuth и Open Id Connect — надеюсь, я смогу помочь с этим с помощью простого примера кода TypeScript
- Подумайте о наилучшем способе перевода примера в Angular, что, я уверен, вы сможете сделать достаточно легко
ФАЙЛ КОНФИГУРАЦИИ
Мне нравится выражать настройки OAuth с помощью конфигурации JSON — см. Этот файл. Вы можете обновить файл в соответствии с вашей конфигурацией, запустить мой пример, и, надеюсь, вы увидите, что токен возвращается.
ОТВЕТСТВЕННОСТЬ КЛИЕНТА API
Когда пользовательский интерфейс вызывает API, он должен сначала получить токен доступа, а пользовательский интерфейс также должен обрабатывать ответы с истекшим сроком действия / 401 — см. Этот класс .
ОТВЕТСТВЕННОСТЬ КЛИЕНТА OAUTH
Клиент API вызывает этот класс для получения токена, который использует клиентскую библиотеку OIDC для управления перенаправлениями, ответами и возвращаемыми токенами.
ПОЯСНИТЕЛЬНОЕ СООБЩЕНИЕ В БЛОГЕ
Смотрите также эти записи, которые я стремился сделать наглядными и удобными для чтения: