From 0942aa873372e5c2d81879c9fb0296a778d29f05 Mon Sep 17 00:00:00 2001 From: SESA469345 Date: Thu, 19 Mar 2020 20:17:29 +0530 Subject: [PATCH 1/5] Fix for Silent refresh --- projects/lib/src/oauth-service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/lib/src/oauth-service.ts b/projects/lib/src/oauth-service.ts index 7f0a204d..6201d750 100644 --- a/projects/lib/src/oauth-service.ts +++ b/projects/lib/src/oauth-service.ts @@ -1455,7 +1455,8 @@ export class OAuthService extends AuthConfig implements OnDestroy { options.customHashFragment.substring(1) : window.location.search; - const parts = this.getCodePartsFromUrl(window.location.search); + // const parts = this.getCodePartsFromUrl(window.location.search); + const parts = this.getCodePartsFromUrl(querySource); const code = parts['code']; const state = parts['state']; From 255bc5ab428922e593e72b34e9e63a331d7ace92 Mon Sep 17 00:00:00 2001 From: SESA469345 Date: Tue, 24 Mar 2020 20:40:45 +0530 Subject: [PATCH 2/5] Revert "Fix for Silent refresh" This reverts commit 0942aa873372e5c2d81879c9fb0296a778d29f05. --- projects/lib/src/oauth-service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/lib/src/oauth-service.ts b/projects/lib/src/oauth-service.ts index 6201d750..7f0a204d 100644 --- a/projects/lib/src/oauth-service.ts +++ b/projects/lib/src/oauth-service.ts @@ -1455,8 +1455,7 @@ export class OAuthService extends AuthConfig implements OnDestroy { options.customHashFragment.substring(1) : window.location.search; - // const parts = this.getCodePartsFromUrl(window.location.search); - const parts = this.getCodePartsFromUrl(querySource); + const parts = this.getCodePartsFromUrl(window.location.search); const code = parts['code']; const state = parts['state']; From 0eab25e7eb9dce253c3a57b2d639d77f0a43d53b Mon Sep 17 00:00:00 2001 From: SESA469345 Date: Tue, 24 Mar 2020 21:02:08 +0530 Subject: [PATCH 3/5] Fall back expires_in for code flow if not received from CIAM --- projects/lib/src/oauth-service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/lib/src/oauth-service.ts b/projects/lib/src/oauth-service.ts index 7f0a204d..0dc4c99d 100644 --- a/projects/lib/src/oauth-service.ts +++ b/projects/lib/src/oauth-service.ts @@ -732,7 +732,7 @@ export class OAuthService extends AuthConfig implements OnDestroy { this.storeAccessTokenResponse( tokenResponse.access_token, tokenResponse.refresh_token, - tokenResponse.expires_in, + tokenResponse.expires_in || this.fallbackAccessTokenExpirationTimeInSec, tokenResponse.scope ); @@ -809,7 +809,7 @@ export class OAuthService extends AuthConfig implements OnDestroy { this.storeAccessTokenResponse( tokenResponse.access_token, tokenResponse.refresh_token, - tokenResponse.expires_in, + tokenResponse.expires_in || this.fallbackAccessTokenExpirationTimeInSec, tokenResponse.scope ); @@ -1579,7 +1579,7 @@ export class OAuthService extends AuthConfig implements OnDestroy { this.storeAccessTokenResponse( tokenResponse.access_token, tokenResponse.refresh_token, - tokenResponse.expires_in, + tokenResponse.expires_in || this.fallbackAccessTokenExpirationTimeInSec, tokenResponse.scope); if (this.oidc && tokenResponse.id_token) { From 4e00ba65690f6c1d8885cc0c62a5d543d5e17a2e Mon Sep 17 00:00:00 2001 From: Varada-Schneider Date: Wed, 25 Mar 2020 09:18:38 +0530 Subject: [PATCH 4/5] There is no explicit way to revoke an access token when the user logs out --- projects/lib/src/auth.config.ts | 5 +++++ projects/lib/src/oauth-service.ts | 33 +++++++++++++++++++++++++++++++ projects/lib/src/types.ts | 3 ++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/projects/lib/src/auth.config.ts b/projects/lib/src/auth.config.ts index f30b0f09..ef6abe74 100644 --- a/projects/lib/src/auth.config.ts +++ b/projects/lib/src/auth.config.ts @@ -64,6 +64,11 @@ export class AuthConfig { */ public tokenEndpoint?: string = null; + /** + * Url of the revocation endpoint as defined by OpenId Connect and OAuth 2. + */ + public revocationEndpoint?: string = null; + /** * Url of the userinfo endpoint as defined by OpenId Connect. */ diff --git a/projects/lib/src/oauth-service.ts b/projects/lib/src/oauth-service.ts index 0dc4c99d..ea062b29 100644 --- a/projects/lib/src/oauth-service.ts +++ b/projects/lib/src/oauth-service.ts @@ -462,6 +462,7 @@ export class OAuthService extends AuthConfig implements OnDestroy { this.discoveryDocumentLoaded = true; this.discoveryDocumentLoadedSubject.next(doc); + this.revocationEndpoint = doc.revocation_endpoint; if (this.sessionChecksEnabled) { this.restartSessionChecksIfStillLoggedIn(); @@ -564,6 +565,14 @@ export class OAuthService extends AuthConfig implements OnDestroy { ); } + errors = this.validateUrlFromDiscoveryDocument(doc.revocation_endpoint); + if (errors.length > 0) { + this.logger.error( + "error validating revocation_endpoint in discovery document", + errors + ); + } + errors = this.validateUrlFromDiscoveryDocument(doc.userinfo_endpoint); if (errors.length > 0) { this.logger.error( @@ -2309,4 +2318,28 @@ export class OAuthService extends AuthConfig implements OnDestroy { return [challange, verifier]; } + + /** + * Revokes the auth token to secure the vulnarability + * of the token issued allowing the authorization server to clean + * up any security credentials associated with the authorization + */ + public revokeTokenAndLogout(): Promise { + const revoke_endpoint = this.revocationEndpoint; + const current_access_token = this.getAccessToken(); + return new Promise((resolve, reject) => { + fetch(revoke_endpoint, { + method: 'POST', + headers: + { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: `token=${current_access_token}` + }).then(res => { + console.log('token successfully revoked'); + this.logOut(); + resolve(res); + }); + }); + } } diff --git a/projects/lib/src/types.ts b/projects/lib/src/types.ts index ce9f8e59..ebb9e653 100644 --- a/projects/lib/src/types.ts +++ b/projects/lib/src/types.ts @@ -138,7 +138,7 @@ export interface ParsedIdToken { */ export interface TokenResponse { access_token: string; - id_token: string; + id_token: string; token_type: string; expires_in: number; refresh_token: string; @@ -188,4 +188,5 @@ export interface OidcDiscoveryDoc { claims_parameter_supported: boolean; service_documentation: string; ui_locales_supported: string[]; + revocation_endpoint: string; } From c799eadbfa616d459af8be1a667499834745d78f Mon Sep 17 00:00:00 2001 From: SESA469345 Date: Sat, 28 Mar 2020 13:50:34 +0530 Subject: [PATCH 5/5] fix(revoketokenandlogout): explicit way to revoke an access token --- projects/lib/src/events.ts | 3 +- projects/lib/src/oauth-service.ts | 81 +++++++++++++++++++++++-------- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/projects/lib/src/events.ts b/projects/lib/src/events.ts index 7349b92c..a19bd8fa 100644 --- a/projects/lib/src/events.ts +++ b/projects/lib/src/events.ts @@ -21,7 +21,8 @@ export type EventType = | 'session_terminated' | 'logout' | 'popup_closed' - | 'popup_blocked'; + | 'popup_blocked' + | 'token_revoke_error'; export abstract class OAuthEvent { constructor(readonly type: EventType) {} diff --git a/projects/lib/src/oauth-service.ts b/projects/lib/src/oauth-service.ts index 07b3259a..f3139b0d 100644 --- a/projects/lib/src/oauth-service.ts +++ b/projects/lib/src/oauth-service.ts @@ -813,7 +813,8 @@ export class OAuthService extends AuthConfig implements OnDestroy { this.storeAccessTokenResponse( tokenResponse.access_token, tokenResponse.refresh_token, - tokenResponse.expires_in || this.fallbackAccessTokenExpirationTimeInSec, + tokenResponse.expires_in || + this.fallbackAccessTokenExpirationTimeInSec, tokenResponse.scope, this.extractRecognizedCustomParameters(tokenResponse) ); @@ -899,7 +900,8 @@ export class OAuthService extends AuthConfig implements OnDestroy { this.storeAccessTokenResponse( tokenResponse.access_token, tokenResponse.refresh_token, - tokenResponse.expires_in || this.fallbackAccessTokenExpirationTimeInSec, + tokenResponse.expires_in || + this.fallbackAccessTokenExpirationTimeInSec, tokenResponse.scope, this.extractRecognizedCustomParameters(tokenResponse) ); @@ -1738,7 +1740,8 @@ export class OAuthService extends AuthConfig implements OnDestroy { this.storeAccessTokenResponse( tokenResponse.access_token, tokenResponse.refresh_token, - tokenResponse.expires_in || this.fallbackAccessTokenExpirationTimeInSec, + tokenResponse.expires_in || + this.fallbackAccessTokenExpirationTimeInSec, tokenResponse.scope, this.extractRecognizedCustomParameters(tokenResponse) ); @@ -2549,26 +2552,62 @@ export class OAuthService extends AuthConfig implements OnDestroy { } /** - * Revokes the auth token to secure the vulnarability - * of the token issued allowing the authorization server to clean - * up any security credentials associated with the authorization - */ + * Revokes the auth token to secure the vulnarability + * of the token issued allowing the authorization server to clean + * up any security credentials associated with the authorization + */ public revokeTokenAndLogout(): Promise { - const revoke_endpoint = this.revocationEndpoint; - const current_access_token = this.getAccessToken(); + let revoke_endpoint = this.revocationEndpoint; + let current_access_token = this.getAccessToken(); + let params = new HttpParams() + .set('token', current_access_token) + .set('token_type_hint', 'access_token'); + + let headers = new HttpHeaders().set( + 'Content-Type', + 'application/x-www-form-urlencoded' + ); + + if (this.useHttpBasicAuth) { + const header = btoa(`${this.clientId}:${this.dummyClientSecret}`); + headers = headers.set('Authorization', 'Basic ' + header); + } + + if (!this.useHttpBasicAuth) { + params = params.set('client_id', this.clientId); + } + + if (!this.useHttpBasicAuth && this.dummyClientSecret) { + params = params.set('client_secret', this.dummyClientSecret); + } + + if (this.customQueryParams) { + for (const key of Object.getOwnPropertyNames(this.customQueryParams)) { + params = params.set(key, this.customQueryParams[key]); + } + } + return new Promise((resolve, reject) => { - fetch(revoke_endpoint, { - method: 'POST', - headers: - { - 'Content-Type': 'application/x-www-form-urlencoded' - }, - body: `token=${current_access_token}` - }).then(res => { - console.log('token successfully revoked'); - this.logOut(); - resolve(res); - }); + if (current_access_token) { + this.http + .post(revoke_endpoint, params, { headers }) + .subscribe( + res => { + this.logOut(); + resolve(res); + this.logger.info('Token successfully revoked'); + }, + err => { + this.logger.error('Error revoking token', err); + this.eventsSubject.next( + new OAuthErrorEvent('token_revoke_error', err) + ); + reject(err); + } + ); + } else { + this.logger.warn('User not logged in to revoke token.'); + } }); } }