OpenID Connect Session Management using an Angular application and IdentityServer4

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

History

2019-09-20: Updated ASP.NET Core 3.0, Angular 8.2.6
2018-06-22: Updated ASP.NET Core 2.1, Angular 6.0.6, ASP.NET Core Identity 2.1

Full history:
https://github.com/damienbod/AspNet5IdentityServerAngularImplicitFlow#history

Other posts in this series:

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/

5 comments

  1. […] OpenID Connect Session Management an Angular application using IdentityServer4 (Damien Bowden) […]

  2. […] OpenID Connect Session Management an Angular application using IdentityServer4  […]

  3. Source code for the implementation

    1. see the link art the start

  4. Should this work across browsers? If a user signs in to a client in chrome and then also in edge, but then signs out from edge which causes a sign out at the OP, should the chrome client detect a checksession changed event since that user is now signed out at the OP?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: