Skip to content

Commit d54deac

Browse files
committed
2 parents 58c6354 + e0035e7 commit d54deac

File tree

8 files changed

+142
-29
lines changed

8 files changed

+142
-29
lines changed

docs-src/implicit-flow.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ This file is loaded into the hidden iframe after getting new tokens. Its only ta
154154
<html>
155155
<body>
156156
<script>
157-
window.parent.postMessage(location.hash || ('#' + location.search), location.origin);
157+
(window.opener || window.parent).postMessage(location.hash || ('#' + location.search), location.origin);
158158
</script>
159159
</body>
160160
</html>
@@ -190,4 +190,4 @@ To automatically refresh a token when/ some time before it expires, just call th
190190
this.oauthService.setupAutomaticSilentRefresh();
191191
```
192192

193-
By default, this event is fired after 75% of the token's life time is over. You can adjust this factor by setting the property ``timeoutFactor`` to a value between 0 and 1. For instance, 0.5 means, that the event is fired after half of the life time is over and 0.33 triggers the event after a third.
193+
By default, this event is fired after 75% of the token's life time is over. You can adjust this factor by setting the property ``timeoutFactor`` to a value between 0 and 1. For instance, 0.5 means, that the event is fired after half of the life time is over and 0.33 triggers the event after a third.

package-lock.json

+30-11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

projects/lib/src/auth.config.ts

+5
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ export class AuthConfig {
6464
*/
6565
public tokenEndpoint?: string = null;
6666

67+
/**
68+
* Names of known parameters sent out in the TokenResponse. https://tools.ietf.org/html/rfc6749#section-5.1
69+
*/
70+
public customTokenParameters?: string[] = [];
71+
6772
/**
6873
* Url of the userinfo endpoint as defined by OpenId Connect.
6974
*/

projects/lib/src/oauth-service.ts

+55-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { Injectable, NgZone, Optional, OnDestroy } from '@angular/core';
1+
import { Injectable, NgZone, Optional, OnDestroy, Inject } from '@angular/core';
22
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
33
import { Observable, Subject, Subscription, of, race, from } from 'rxjs';
44
import { filter, delay, first, tap, map, switchMap, debounceTime } from 'rxjs/operators';
5+
import { DOCUMENT } from '@angular/common';
56

67
import {
78
ValidationHandler,
@@ -91,6 +92,7 @@ export class OAuthService extends AuthConfig implements OnDestroy {
9192
protected urlHelper: UrlHelperService,
9293
protected logger: OAuthLogger,
9394
@Optional() protected crypto: HashHandler,
95+
@Inject(DOCUMENT) private document: Document,
9496
) {
9597
super();
9698

@@ -733,7 +735,8 @@ export class OAuthService extends AuthConfig implements OnDestroy {
733735
tokenResponse.access_token,
734736
tokenResponse.refresh_token,
735737
tokenResponse.expires_in,
736-
tokenResponse.scope
738+
tokenResponse.scope,
739+
this.extractRecognizedCustomParameters(tokenResponse)
737740
);
738741

739742
this.eventsSubject.next(new OAuthSuccessEvent('token_received'));
@@ -810,7 +813,8 @@ export class OAuthService extends AuthConfig implements OnDestroy {
810813
tokenResponse.access_token,
811814
tokenResponse.refresh_token,
812815
tokenResponse.expires_in,
813-
tokenResponse.scope
816+
tokenResponse.scope,
817+
this.extractRecognizedCustomParameters(tokenResponse)
814818
);
815819

816820
this.eventsSubject.next(new OAuthSuccessEvent('token_received'));
@@ -1400,7 +1404,8 @@ export class OAuthService extends AuthConfig implements OnDestroy {
14001404
accessToken: string,
14011405
refreshToken: string,
14021406
expiresIn: number,
1403-
grantedScopes: String
1407+
grantedScopes: String,
1408+
customParameters?: Map<string, string>
14041409
): void {
14051410
this._storage.setItem('access_token', accessToken);
14061411
if (grantedScopes) {
@@ -1417,6 +1422,11 @@ export class OAuthService extends AuthConfig implements OnDestroy {
14171422
if (refreshToken) {
14181423
this._storage.setItem('refresh_token', refreshToken);
14191424
}
1425+
if (customParameters) {
1426+
customParameters.forEach((value : string, key: string) => {
1427+
this._storage.setItem(key, value);
1428+
});
1429+
}
14201430
}
14211431

14221432
/**
@@ -1455,7 +1465,7 @@ export class OAuthService extends AuthConfig implements OnDestroy {
14551465
options.customHashFragment.substring(1) :
14561466
window.location.search;
14571467

1458-
const parts = this.getCodePartsFromUrl(window.location.search);
1468+
const parts = this.getCodePartsFromUrl(querySource);
14591469

14601470
const code = parts['code'];
14611471
const state = parts['state'];
@@ -1580,7 +1590,8 @@ export class OAuthService extends AuthConfig implements OnDestroy {
15801590
tokenResponse.access_token,
15811591
tokenResponse.refresh_token,
15821592
tokenResponse.expires_in,
1583-
tokenResponse.scope);
1593+
tokenResponse.scope,
1594+
this.extractRecognizedCustomParameters(tokenResponse));
15841595

15851596
if (this.oidc && tokenResponse.id_token) {
15861597
this.processIdToken(tokenResponse.id_token, tokenResponse.access_token).
@@ -2084,6 +2095,16 @@ export class OAuthService extends AuthConfig implements OnDestroy {
20842095
return false;
20852096
}
20862097

2098+
/**
2099+
* Retrieve a saved custom property of the TokenReponse object. Only if predefined in authconfig.
2100+
*/
2101+
public getCustomTokenResponseProperty(requestedProperty: string): any {
2102+
return this._storage && this.config.customTokenParameters
2103+
&& (this.config.customTokenParameters.indexOf(requestedProperty) >= 0)
2104+
&& this._storage.getItem(requestedProperty) !== null
2105+
? JSON.parse(this._storage.getItem(requestedProperty)) : null;
2106+
}
2107+
20872108
/**
20882109
* Returns the auth-header that can be used
20892110
* to transmit the access_token to a service
@@ -2095,10 +2116,11 @@ export class OAuthService extends AuthConfig implements OnDestroy {
20952116
/**
20962117
* Removes all tokens and logs the user out.
20972118
* If a logout url is configured, the user is
2098-
* redirected to it.
2119+
* redirected to it with optional state parameter.
20992120
* @param noRedirectToLogoutUrl
2121+
* @param state
21002122
*/
2101-
public logOut(noRedirectToLogoutUrl = false): void {
2123+
public logOut(noRedirectToLogoutUrl = false, state = ''): void {
21022124
const id_token = this.getIdToken();
21032125
this._storage.removeItem('access_token');
21042126
this._storage.removeItem('id_token');
@@ -2111,7 +2133,9 @@ export class OAuthService extends AuthConfig implements OnDestroy {
21112133
this._storage.removeItem('access_token_stored_at');
21122134
this._storage.removeItem('granted_scopes');
21132135
this._storage.removeItem('session_state');
2114-
2136+
if (this.config.customTokenParameters) {
2137+
this.config.customTokenParameters.forEach(customParam => this._storage.removeItem(customParam));
2138+
}
21152139
this.silentRefreshSubject = null;
21162140

21172141
this.eventsSubject.next(new OAuthInfoEvent('logout'));
@@ -2151,6 +2175,10 @@ export class OAuthService extends AuthConfig implements OnDestroy {
21512175
const postLogoutUrl = this.postLogoutRedirectUri || this.redirectUri;
21522176
if (postLogoutUrl) {
21532177
params = params.set('post_logout_redirect_uri', postLogoutUrl);
2178+
2179+
if (state) {
2180+
params = params.set('state', state);
2181+
}
21542182
}
21552183

21562184
logoutUrl =
@@ -2180,14 +2208,14 @@ export class OAuthService extends AuthConfig implements OnDestroy {
21802208
this.clearIdTokenTimer();
21812209

21822210
this.removeSilentRefreshEventListener();
2183-
const silentRefreshFrame = document.getElementById(this.silentRefreshIFrameName);
2211+
const silentRefreshFrame = this.document.getElementById(this.silentRefreshIFrameName);
21842212
if (silentRefreshFrame) {
21852213
silentRefreshFrame.remove();
21862214
}
21872215

21882216
this.stopSessionCheckTimer();
21892217
this.removeSessionCheckEventListener();
2190-
const sessionCheckFrame = document.getElementById(this.sessionCheckIFrameName);
2218+
const sessionCheckFrame = this.document.getElementById(this.sessionCheckIFrameName);
21912219
if (sessionCheckFrame) {
21922220
sessionCheckFrame.remove();
21932221
}
@@ -2305,8 +2333,21 @@ export class OAuthService extends AuthConfig implements OnDestroy {
23052333

23062334
const verifier = await this.createNonce();
23072335
const challengeRaw = await this.crypto.calcHash(verifier, 'sha-256');
2308-
const challange = base64UrlEncode(challengeRaw);
2309-
2310-
return [challange, verifier];
2336+
const challenge = base64UrlEncode(challengeRaw);
2337+
2338+
return [challenge, verifier];
2339+
}
2340+
2341+
private extractRecognizedCustomParameters(tokenResponse: TokenResponse): Map<string, string> {
2342+
let foundParameters: Map<string, string> = new Map<string, string>();
2343+
if (!this.config.customTokenParameters) {
2344+
return foundParameters;
2345+
}
2346+
this.config.customTokenParameters.forEach((recognizedParameter: string) => {
2347+
if (tokenResponse[recognizedParameter]) {
2348+
foundParameters.set(recognizedParameter, JSON.stringify(tokenResponse[recognizedParameter]));
2349+
}
2350+
});
2351+
return foundParameters;
23112352
}
23122353
}

projects/sample/src/app/auth-code-flow.config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export const authCodeFlowConfig: AuthConfig = {
99
? '/#/index.html'
1010
: '/index.html'),
1111

12+
silentRefreshRedirectUri: `${window.location.origin}/silent-refresh.html`,
13+
1214
// The SPA's id. The SPA is registerd with this id at the auth-server
1315
// clientId: 'server.code',
1416
clientId: 'spa',

0 commit comments

Comments
 (0)