The article shows how the OpenID Connect Session Management can be implemented in an Angular application. The OpenID Connect Session Management 1.0 provides a way of monitoring the user session on the server using iframes. IdentityServer4 implements the server side of the specification. This does not monitor the lifecycle of the tokens used in the browser application. This session only monitors the server session. This has nothing to do with the OpenID tokens used by the SPA application.
Code: https://github.com/damienbod/AspNet5IdentityServerAngularImplicitFlow
Code: Angular auth module
Other posts in this series:
- OAuth2 Implicit Flow with Angular and ASP.NET Core 1.1 IdentityServer4
- Authorization Policies and Data Protection with IdentityServer4 in ASP.NET Core
- AngularJS OpenID Connect Implicit Flow with IdentityServer4
- Angular OpenID Connect Implicit Flow with IdentityServer4
- Secure file download using IdentityServer4, Angular and ASP.NET Core
- Angular secure file download without using an access token in URL or cookies
- Full Server logout with IdentityServer4 and OpenID Connect Implicit Flow
- IdentityServer4, Web API and Angular in a single project
- Extending Identity in IdentityServer4 to manage users in ASP.NET Core
- Implementing a silent token renew in Angular for the OpenID Connect Implicit flow
- OpenID Connect Session Management using an Angular application and IdentityServer4
The OidcSecurityCheckSession class implements the Session Management from the specification. The init function creates an iframe and adds it to the window document in the DOM. The iframe uses the ‘authWellKnownEndpoints.check_session_iframe’ value, which is the connect/checksession API got from the ‘.well-known/openid-configuration’ service.
The init function also adds the event for the message, which is specified in the OpenID Connect Session Management documentation.
init() { this.sessionIframe = window.document.createElement('iframe'); this.oidcSecurityCommon.logDebug(this.sessionIframe); this.sessionIframe.style.display = 'none'; this.sessionIframe.src = this.authWellKnownEndpoints.check_session_iframe; window.document.body.appendChild(this.sessionIframe); this.iframeMessageEvent = this.messageHandler.bind(this); window.addEventListener('message', this.iframeMessageEvent, false); return Observable.create((observer: Observer<any>) => { this.sessionIframe.onload = () => { observer.next(this); observer.complete(); } }); }
The pollServerSession function, posts a message every 3 seconds to the iframe which checks if the session on the server has been changed. The session_state is the value returned in the HTTP callback from a successful authorization.
pollServerSession(session_state: any, clientId: any) { let source = Observable.timer(3000, 3000) .timeInterval() .pluck('interval') .take(10000); let subscription = source.subscribe(() => { this.oidcSecurityCommon.logDebug(this.sessionIframe); this.sessionIframe.contentWindow.postMessage(clientId + ' ' + session_state, this.authConfiguration.stsServer); }, (err: any) => { this.oidcSecurityCommon.logError('pollServerSession error: ' + err); }, () => { this.oidcSecurityCommon.logDebug('checksession pollServerSession completed'); }); }
The messageHandler handles the callback from the iframe. If the server session has changed, the output onCheckSessionChanged event is triggered.
private messageHandler(e: any) { if (e.origin === this.authConfiguration.stsServer && e.source === this.sessionIframe.contentWindow ) { if (e.data === 'error') { this.oidcSecurityCommon.logWarning('error from checksession messageHandler'); } else if (e.data === 'changed') { this.onCheckSessionChanged.emit(); } else { this.oidcSecurityCommon.logDebug(e.data + ' from checksession messageHandler'); } } }
The onCheckSessionChanged is a public EventEmitter output for this provider.
@Output() onCheckSessionChanged: EventEmitter<any> = new EventEmitter<any>(true);
The OidcSecurityService provider subscribes to the onCheckSessionChanged event and uses its onCheckSessionChanged function to handle this event.
this.oidcSecurityCheckSession.onCheckSessionChanged.subscribe(() => { this.onCheckSessionChanged(); });
After a successful login, and if the tokens are valid, the client application checks if the checksession should be used, and calls the init method and subscribes to it. When ready, it uses the pollServerSession function to activate the monitoring.
if (this.authConfiguration.start_checksession) { this.oidcSecurityCheckSession.init().subscribe(() => { this.oidcSecurityCheckSession.pollServerSession( result.session_state, this.authConfiguration.client_id ); }); }
The onCheckSessionChanged function sets a public boolean which can be used to implement the required application logic when the server sesion has changed.
private onCheckSessionChanged() { this.oidcSecurityCommon.logDebug('onCheckSessionChanged'); this.checkSessionChanged = true; }
In this demo, the navigation bar allows to Angular application to refresh the session if the server session has changed.
<li> <a class="navigationLinkButton" *ngIf="securityService.checkSessionChanged" (click)="refreshSession()">Refresh Session</a> </li>
When the application is started, the unchanged message is returned.
Then open the server application in a tab in the same browser session, and logout.
And the client application notices tht the server session has changed and can react as required.
Links:
http://openid.net/specs/openid-connect-session-1_0-ID4.html
http://docs.identityserver.io/en/release/
