Skip to content

JavaScript library to assist with binding sessions between an OIDC OP and RP

License

Notifications You must be signed in to change notification settings

robin-karlsson/oidcSessionCheck

 
 

Repository files navigation

OIDC Session Check

JavaScript library to assist with binding sessions between an OIDC OP and RP

Purpose

When you use OpenID Connect to handle authentication in your application, there are two parties involved - the "OpenID Provider" (OP) and the Relying Party (RP).

The OP is a web server, and it handles authenticating the user. Oftentimes, when the user logs into the OP with a browser there is a session created there. The OP will set a cookie in the user's browser, which allows the user to be remembered the next time they visit the OP.

The RP is an application which uses the identity details returned from the OP. Typically, it uses these details to construct it's own session. The session at the RP does not normally have any relationship to the session at the OP; if a user logs-out of one, it doesn't normally cause the session to end on the other.

When RP applications are owned by the same organization as the OP, there is often a desire to make the various sessions more closely associated. Doing so presents the user with a seamless experience as they navigate between each app. This library is designed to make that session association easier to establish.

How it works

The cookie which identifies the session at the OP is assumed to be unavailable to the RP (due to differences in their respective domains). However, there is a mechanism available as part of OIDC which allows that cookie to be used. OIDC works by redirection between the RP and OP - the browser makes a request to the OP's "authorization endpoint"; when finished, the OP redirects the browser back to the RP's "redirect_uri" along with some relevant data. This is the fundamental mechanism by which the RP can learn about the state of the session in the OP.

This redirection needs to be non-intrusive; it shouldn't be noticeable to the user while they are interacting with the RP. The most practical way to accomplish this is to make use of hidden iframes within the RP application. The RP can set the iframe src value to be the OP authorization endpoint (along with the other required parameters); when this loads, the OP session cookie will be passed along as per normal browser behavior.

The simplest strategy for this interaction is to use the "Implicit Flow" along with the "id_token" response type. Doing so will allow the direct response to the redirect_uri (passing data within the hash fragment of the URL); successful responses will only include the id_token. See the specification for Implicit Flow Authentication Requests for more details.

Since this use-case involves simple message passing (with no direct user interaction) there is one more parameter that is passed along in the authentication request - prompt=none. By including this parameter, the response will either succeed immediately or fail immediately - there won't be any "prompt" for the user to log in or anything else.

The data that is passed back to the RP's redirect_uri is either an "id_token" (which contains the logged-in user's details) or an "error" parameter. The state of the user's session on the OP will determine which of these values are returned. These must be read by the redirect_uri page, and based on the values it must use the postMessage API to communicate the results to the RP parent frame.

Using this library

This library automates the iframe creation and message passing between your RP and the OP. It allows you to easily monitor the state of the OP session - you simply need to provide a few details about your operating environment and decide when you want those checks to be made.

The "SessionCheck" module can be loaded in several ways:

  • using a global variable by directly including a script tag: <script src="sessionCheckGlobal.js"></script>
  • using CommonJS modules: var SessionCheck = require('sessionCheck');

Setting up the environment:

var sessionCheck = new SessionCheck({
    clientId: "myRP",
    opUrl: "https://login.example.com/oauth2/authorize",
    subject: loggedInUsername,
    invalidSessionHandler: function (reason) {
        logoutFromRP();
    },
    sessionClaimsHandler: function (claims) {
        // do something interesting with the current claims
    },
    // optional
    cooldownPeriod: 5,
    // optional
    redirectUri: "sessionCheck.html",
    // optional
    scope: "openid"
});

Examples for when to check the session:

// check with various events based on user interaction:
document.addEventListener("click", function () {
    sessionCheck.triggerSessionCheck();
});
document.addEventListener("keypress", function () {
    sessionCheck.triggerSessionCheck();
});

// check every minute (not recommended)
setInterval(function () {
    sessionCheck.triggerSessionCheck();
}, 60000);

Details you need to provide:

  • subject - The user currently logged into the RP
  • clientId - The id of this RP client within the OP
  • opUrl - Full URL to the OP Authorization Endpoint
  • invalidSessionHandler - function to be called once any problem with the session is detected, with reason to invalid session included
  • sessionClaimsHandler [optional] - function to be called after every successful session check, with latest claims included
  • redirectUri [default: sessionCheck.html] - The redirect uri registered in the OP for session-checking purposes
  • cooldownPeriod [default: 5] - Minimum time (in seconds) between requests to the opUrl
  • scope [default: openid] - OIDC scope names (space separated) to be requested

This library requires that your user is already authenticated prior to creating an instance of it. You must provide the current username of that user - this will be checked against the "subject" claim within the id_token that is returned by the OP. If they don't match, it is assumed that the OP and RP sessions are out of sync, and that will trigger the invalidSessionHandler.

The invalidSessionHandler will be called whenever there is a problem detected from the OP response. The intent for this handler is for you to trigger a local log-out event, so that the current RP session is terminated (likely to result in an interactive redirection to the OP so as to obtain a new RP session).

The sessionClaimsHandler will be called every time the session check occurs. It will include the claims from the id_token. The intent for this handler is to allow you to respond to various claims that might be included in the id_token - for example, you could use the "exp" claim to warn the user when their session will end. This handler is optional.

You will need to make sure the redirect_uri used for this is registered with the OP. By default, you can use the included sessionCheck.html as the uri to register. Whatever you choose to use, be sure the sessionCheckFrame.js code is included within it.

It is up to you to decide how frequently and in which circumstances you want to check the OP for session status changes. The "cooldownPeriod" setting determines the maximum frequency you want to check the OP. Regardless of how many times you call triggerSessionCheck() within that period, it will only be checked once. As a result, you can call this using any combination of events without worrying about flooding the OP with requests.

Cleaning up the environment

Once the SessionCheck instance is no longer needed you should destroy() and nullify the instance to garbase collect the instance, the related iframe, and global event handlers.

var sessionCheck = new SessionCheck(config);
// use sessionCheck... then when you're done with it...
sessionCheck.destroy();
sessionCheck = null;

License

MIT. Copyright ForgeRock, Inc. 2020

About

JavaScript library to assist with binding sessions between an OIDC OP and RP

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 99.6%
  • HTML 0.4%