REST APIs are everywhere now, and for good reason. Say you are a savvy service provider – you don’t want to only offer a web application that requires direct user interaction. You know that business value comes from integration and partnerships, and so you publish your services as a REST API. By providing this API, you enable your services to be used by many different applications, expanding your reach and making more money as a result.
Security is critical for every form of service you offer, including your REST API. At the same time, you know that for an API to be successful, it has to be easy to work with. You balance security and usability by relying on standards – specifically, OAuth2. This is a great choice, because there is a broad ecosystem available to support the publication and consumption of OAuth2-based APIs (see “The ForgeRock Platform” for details). There are many options to choose from in the market for client software; these are used to request access tokens and then supply them as part of the REST API calls. If you are unsure how OAuth2 works, take a look at my past article “The OAuth2 Apartment Building“.
Your own web applications are still very important – direct user interaction with your services is absolutely needed. This presents you with a choice – build your own web applications as OAuth2-based clients that use your REST APIs similar to how third-parties would use them, or build something proprietary which does not use those APIs. It may initially be easier to build an application without relying on the REST API; designing a complete REST API that is suitable for all use-cases can be hard. The problem with this option is that now you are faced with maintaining multiple points of entry into your system – the REST API used by third-parties and your proprietary web application. Inevitably, one of those will be less robust than the other, and you will have essentially doubled your work to maintain them. The best approach for the long term is to build your web applications using the same OAuth2-based REST APIs that third-parties use.
Presenting a single sign-on experience for your users is as important as ever. When they login anywhere within your platform, they have a reasonable expectation that the session they started will be good everywhere they browse within your platform. Likewise, they have an expectation that whenever they logout from anywhere within your platform, their session will be terminated everywhere within your platform. Since your web application is being built on top of an OAuth2-based REST API, you will need to find a way to provide this kind of seamless session experience in that environment.
OpenID Connect (OIDC) is the fundamental technique required to achieve single sign-on in this context. OIDC is a standard means to allow an “OpenID Provider” (OP) to handle authentication for a user on behalf of a “relying party” (RP) application (for a high-level overview of how OIDC works, take a look at my previous article “The OpenID Connect Neighborhood“). Using OIDC, you can obtain the two things you need: a valid OAuth2 access token to submit to the REST API and information about the user that is currently logged in. The RP needs the authenticated user’s identity details and gets them in the form of an id token from the OP. By virtue of being an extension to OAuth2, logging in with OIDC will also allow the RP to obtain the access token. ForgeRock Access Management is built to operate as an OpenID Provider.
Initially logging in is a well-established part of the OIDC process. The first time a user needs to login to any of your web applications they will be redirected to the OP to do so. The OP is responsible for authenticating them and then maintaining session details about that authentication. The OP will set a cookie in the user’s browser that identifies this session, which is only readable by the OP. The OP will then redirect the user back to the RP so that it can fetch the tokens it needs.
Keeping tokens current is the main challenge that your web applications need to solve for single sign-on. By “current” I mean that they need to stay in sync with the session established within the OP – when that session is valid, then the RP tokens should be valid. When that session ends, the tokens used by the RP should be revoked. The problem is that this session identifier lives in the user’s browser under the OP domain and is completely unavailable to the RP. This makes monitoring for changes to that session difficult for the RP. Fortunately, there is a trick that browsers can use to overcome these limitations.
Hidden iframes within a web application provide a very powerful means of monitoring for OP session status. These are basically non-interactive browser contexts which can leverage redirection to pass messages between the OP and the RP. By embedding an iframe within your web application you can periodically use it to call the OP’s authorization endpoint with the “prompt=none” URL parameter. When the iframe loads this URL, it will include the OP session cookie as part of the request; this is how the OP will be able to recognize the session. The authorization endpoint will always respond by redirecting the frame to your specified “redirect_uri” location, along with additional parameters which tell you details about the state of the user’s session. If the session is still valid, the parameters will allow you to determine the currently logged-in user from the id token details. If the session has expired, then the response will include error messages such as “interaction_required“. When the iframe has these messages available, it can pass information about them back to the parent web application frame using the browser’s postMessage API.
Deciding when to check the session at the OP using the hidden iframe is a topic of debate. The draft specification for OpenID Connect Session Management states:
[I]t is possible to repeat the Authentication Request with prompt=none. However, this causes network traffic and this is problematic on the mobile devices that are becoming increasingly popular.
Another problem is simply adoption – the value to compare against when monitoring for changes in the OP frame is an extra return parameter from the authorization response (called session_state). Most OIDC RP libraries do not recognize this parameter, and so would have to be altered to support it.
The most critical problem with this specification is the concern it has regarding mobile traffic; this was first expressed in August 2012 as part of draft 08, back when mobile networks were merely “increasingly popular”. Now they have become much more robust and ubiquitous. The relative cost of a periodic network request to check session status at the OP is much lower, whereas the cost of the specification in terms of complexity remains high.
The simplest solution would be to just use a setInterval call to re-initiate the authentication request every few seconds (or minutes, if so desired). This would happen regardless of user interaction in the RP, and it would be triggered at whatever frequency you configure. This approach, while easy, does have the downside of causing more load on the system – both in terms of network overhead and work for the OP.
Another option is to use an event-based strategy – basically, only check the session status when the user does something within the RP. This could be page transitions, network requests, key presses or mouse clicks. Whatever events you choose to monitor, you will want to be sure not to issue more than one session check request within a particular window of time (say, every few seconds at the minimum). This is probably the best option, since it won’t overwhelm the network or the OP, and yet still provides very quick feedback to the user when their session expires.
Handling an expired session within the RP is pretty straightforward. Just revoke the access token using the mechanism described in the OAuth 2.0 Token Revocation specification and call the end session endpoint with the id token. Afterwards, simply delete the tokens from your RP memory and redirect the user to a page that they can access without credentials (this might end up just being the OP login page).
The general pattern of requests for this process looks like this:
The ForgeRock Platform
You can use the ForgeRock Identity Platform to build the infrastructure needed for this environment. ForgeRock Access Management operates as the OpenID Provider (OP) – it offers strong authentication, maintains sessions and issues both id and access tokens. ForgeRock Identity Gateway can protect your REST APIs by acting as the Resource Server (RS). It does this by intercepting requests to your APIs; before forwarding the request along, it will read the token from the request and perform token introspection via a call to AM. If AM indicates that the access token has the necessary scopes, the request will be forwarded to the underlying REST API.
The Ideal Relying Party
This approach for session management could be used for any type of OIDC RP that uses the browser as a user agent. The only real technical requirement is it can embed a non-interactive browser context (such as a hidden iframe) with access to the OP session cookie. However, when your web application is designed to work exclusively with your OAuth2 REST APIs as the back-end, you will probably find that the best choice for building your RP is using a pure front-end technology such as a Single-Page Application (i.e. “SPA”). This is because doing so will allow you to avoid having to run an additional application server that is only operating as a proxy to your REST API.
Public clients have two types of grants available to implement – Authorization Code and Implicit. Based on the descriptions in the specification, it may appear that a SPA should be built using the implicit grant; however, industry trends and best current practices that have emerged since the initial spec was written suggest that this is not the best choice after all. Instead, use of the authorization code grant as a public client is considered more secure (most notably by avoiding presence of the access token in browser URL history).
PKCE is an extension to OAuth2 that is designed specifically to make the authorization code grant even more secure for public clients. Essentially, PKCE prevents a malicious third party from using a public client’s authorization code to obtain an access token. While it should be very difficult to intercept an authorization code served over HTTPS, using PKCE provides a valuable additional layer of protection.
To summarize, the ideal relying party for your REST APIs will be a single-page app that uses PKCE to obtain the tokens and periodically checks that the session at the OP is still valid.
ForgeRock has published two open source libraries to help make building this sort of RP as easy as possible:
AppAuth Helper is a wrapper around the AppAuth-JS library that makes it easy to use in the context of a Single-Page App. AppAuth-JS is designed to support PKCE, which makes it a good choice for obtaining tokens in a public client. This helper automates the usage of AppAuth-JS for token acquisition and renewal, providing a very seamless login and token management experience.
OIDC Session Check is a library designed to make it easy to add the session-checking hidden iframe into your web application RP. It can be added into any web-based app (SPA or not) – the only requirement is that you supply the username of the user that is currently logged in. Then you only have to decide when to check the session – based on a regular poll and/or based on various events.