Skip to content

Commit 4bf8901

Browse files
committed
feat(session checks): Session checks work now for code flow too. Pls see Docs for details.
1 parent 71b705c commit 4bf8901

10 files changed

+82
-29
lines changed

docs-src/popup.md

-10
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,4 @@ Thanks to a great community contribution, this library also supports logging the
1010
Also, for your ``silent-regfesh.html``, make sure you are also targeting
1111
``window.opener`` and fall back to ``window.parent``:
1212

13-
```html
14-
<html>
15-
<body>
16-
<script>
17-
(window.opener || window.parent).postMessage(location.hash || ('#' + location.search), location.origin);
18-
</script>
19-
</body>
20-
</html>
21-
```
22-
2313
**Please note**: IE sets opener to null under specific security settings. This prevents making this work.

docs-src/session-checks.md

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ export const authConfig: AuthConfig = {
2929
}
3030
```
3131

32+
## Refresh
33+
34+
Please note that the lib performs a token refresh when the session changes to get the newest information about the current session. When using implicit flow, this means you have to configure [silent refresh](./silent-refresh.html); when using code flow you either need silent refresh or a [refresh token](./refreshing-a-token.html).
35+
36+
If using refresh tokens, your Auth Server needs to bind them to the current session's lifetime. Unfortunately, the used version of Identity Server 4, shown in the docs and in the example applications, does not support this at the moment.
37+
3238
## Events
3339
To get notified, you can hook up for the event ``session_terminated``:
3440

docs-src/silent-refresh.md

+25
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,31 @@ This file is loaded into the hidden iframe after getting new tokens. Its only ta
5858
</html>
5959
```
6060

61+
This simple implementation within silent-refresh.html is sufficient in most cases. It takes care of the hash fragment as well as of the query string (property search). For **edge cases** you need to check if the received hash fragment is a token response. For this, please go with the following **more advanced implementation**:
62+
63+
```html
64+
<html>
65+
<body>
66+
<script>
67+
var checks = [/[\?|&|#]code=/, /[\?|&|#]error=/, /[\?|&|#]token=/, /[\?|&|#]id_token=/];
68+
69+
function isResponse(str) {
70+
var count = 0;
71+
if (!str) return false;
72+
for(var i=0; i<checks.length; i++) {
73+
if (str.match(checks[i])) return true;
74+
}
75+
return false;
76+
}
77+
78+
var message = isResponse(location.hash) ? location.hash : '#' + location.search;
79+
80+
(window.opener || window.parent).postMessage(message, location.origin);
81+
</script>
82+
</body>
83+
</html>
84+
```
85+
6186
Please make sure that this file is copied to your output directory by your build task. When using the CLI you can define it as an asset for this. For this, you have to add the following line to the file ``.angular-cli.json``:
6287

6388
```JSON

projects/lib/src/oauth-service.ts

+18-11
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,9 @@ export class OAuthService extends AuthConfig implements OnDestroy {
10901090
'wrong origin',
10911091
origin,
10921092
'expected',
1093-
issuer
1093+
issuer,
1094+
'event',
1095+
e
10941096
);
10951097

10961098
return;
@@ -1127,10 +1129,20 @@ export class OAuthService extends AuthConfig implements OnDestroy {
11271129
}
11281130

11291131
protected handleSessionChange(): void {
1130-
/* events: session_changed, relogin, stopTimer, logged_out*/
11311132
this.eventsSubject.next(new OAuthInfoEvent('session_changed'));
11321133
this.stopSessionCheckTimer();
1133-
if (this.silentRefreshRedirectUri) {
1134+
1135+
if (!this.useSilentRefresh && this.responseType === 'code') {
1136+
this.refreshToken()
1137+
.then(_ => {
1138+
this.debug('token refresh after session change worked');
1139+
})
1140+
.catch(_ => {
1141+
this.debug('token refresh did not work after session changed');
1142+
this.eventsSubject.next(new OAuthInfoEvent('session_terminated'));
1143+
this.logOut(true);
1144+
});
1145+
} else if (this.silentRefreshRedirectUri) {
11341146
this.silentRefresh().catch(_ =>
11351147
this.debug('silent refresh failed after session changed')
11361148
);
@@ -1519,15 +1531,10 @@ export class OAuthService extends AuthConfig implements OnDestroy {
15191531
return Promise.reject(event);
15201532
}
15211533

1534+
this.storeSessionState(sessionState);
1535+
15221536
if (code) {
1523-
return new Promise((resolve, reject) => {
1524-
this.getTokenFromCode(code, options).then(result => {
1525-
this.storeSessionState(sessionState);
1526-
resolve();
1527-
}).catch(err => {
1528-
reject(err);
1529-
});
1530-
});
1537+
return this.getTokenFromCode(code, options).then(_ => null);
15311538
} else {
15321539
return Promise.resolve();
15331540
}

projects/sample/src/app/app.component.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export class AppComponent {
3030
console.debug('state', this.oauthService.state);
3131
this.oauthService.loadUserProfile();
3232
});
33-
3433
}
3534

3635
private configureCodeFlow() {
@@ -39,7 +38,7 @@ export class AppComponent {
3938
this.oauthService.loadDiscoveryDocumentAndTryLogin();
4039

4140
// Optional
42-
this.oauthService.setupAutomaticSilentRefresh();
41+
// this.oauthService.setupAutomaticSilentRefresh();
4342

4443
}
4544

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

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export const authCodeFlowConfig: AuthConfig = {
4444

4545
showDebugInformation: true,
4646

47+
sessionChecksEnabled: true,
48+
4749
timeoutFactor: 0.01,
4850
// disablePKCI: true,
4951

projects/sample/src/app/auth.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const authConfig: AuthConfig = {
2828

2929
showDebugInformation: true,
3030

31-
sessionChecksEnabled: false,
31+
sessionChecksEnabled: true,
3232

3333
// timeoutFactor: 0.01,
3434
};

projects/sample/src/app/home/home.component.html

+13-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ <h2>Login with Implicit Flow in popup</h2>
3535
<button class="btn btn-default" (click)="loginImplicitInPopup()">Login</button>
3636
<button class="btn btn-default" (click)="logout()">Logout</button>
3737
</p>
38-
<b>Username/Password:</b> max/geheim
38+
<p>
39+
<b>Username/Password:</b> max/geheim
40+
</p>
41+
<p>
42+
<b>Note:</b> When using IE, some security settings block the communication with popups. This prevents that this feature works.
43+
</p>
3944
</div>
4045
</div>
4146

@@ -57,7 +62,13 @@ <h2>Login with Code Flow in popup</h2>
5762
<button class="btn btn-default" (click)="loginCodeInPopup()">Login</button>
5863
<button class="btn btn-default" (click)="logout()">Logout</button>
5964
</p>
60-
<b>Username/Password:</b> alice/alice
65+
<p>
66+
<b>Username/Password:</b> alice/alice
67+
</p>
68+
<p>
69+
<b>Note:</b> When using IE, some security settings block the communication with popups. This prevents that this feature works.
70+
</p>
71+
6172
</div>
6273
</div>
6374

projects/sample/src/flags.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ export const useHash = false;
44

55
// Set this to true, to use silent refresh; otherwise the example
66
// uses the refresh_token via an AJAX coll to get new tokens.
7-
export const useSilentRefreshForCodeFlow = false;
7+
export const useSilentRefreshForCodeFlow = true;
+15-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
<html>
22
<body>
33
<script>
4-
(window.opener || window.parent).postMessage(location.hash || ('#' + location.search), location.origin);
4+
var checks = [/[\?|&|#]code=/, /[\?|&|#]error=/, /[\?|&|#]token=/, /[\?|&|#]id_token=/];
5+
6+
function isResponse(str) {
7+
var count = 0;
8+
if (!str) return false;
9+
for(var i=0; i<checks.length; i++) {
10+
if (str.match(checks[i])) return true;
11+
}
12+
return false;
13+
}
14+
15+
var message = isResponse(location.hash) ? location.hash : '#' + location.search;
16+
17+
(window.opener || window.parent).postMessage(message, location.origin);
518
</script>
619
</body>
7-
</html>
20+
</html>

0 commit comments

Comments
 (0)