OpenID Connect is an emerging authentication protocol defined on top of OAuth 2.0 protocol. The complete protocol suite consists of a series of documents.
Here, we are going to focus on one such document, i.e OpenID Connect Session Management and how that will be supported with WSO2 IS 5.2.0, which will be released soon.
OpenID Connect Session Management specification, defines a way for a Relying Party (RP), to monitor the login status of an end user with an OpenID Connect Provider (OP) minimizing the network traffic. The specification proposes to use two hidden iframes, one at the RP (RP iframe) and the other page is served by the OP (OP iframe) and it is loaded from an invisible iframe at the RP side. These iframes synchronizes the session state between the OP and RP such that the RP gets to know when session state has changed and re-authenticates with a passive request. If an authentication failure or unsuccessful response is received RP will handle that as a logout of the end user.
Please refer the specification for more detail.
So let’s see how WSO2 IS 5.2.0 will support for OIDC Session Management.
Basically, there are two endpoints exposed to support this feature.
OIDC Logout Endpoint:
https://<IS_SERVER_HOST>:<IS_SERVER_PORT>/oidc/logout
Ex:
For a fresh server running in local the endpoint URL will be as below.
https://localhost:9443/oidc/logout
By invoking this endpoint a RP can notify the OP to logout an end user from the OP. Due to some implementation limitations for the moment WSO2 IS 5.2.0 will not support the query parameters defined in the specification. Thus, a RP will not be able to request to redirect the user agent back to the RP after a logout has been performed with ‘post_logout_redirect_uri’ query parameter. But, these will be addressed soon, with the next release.
OIDC Session Iframe Endpoint:
https://<IS_SERVER_HOST>:<IS_SERVER_POST>/oidc/checksession?client_id=<CLIENT_ID>
Ex:
For a fresh server running in local the endpoint URL will be as below.
https://localhost:9443/oidc/checksession?client_id=2PX8XTppZBa6FYRZ6wqFs_o0mGYa
This endpoint provides the OP iframe, that supports session state synchronization with RP client via HTML5 postMessage API. This page should be loaded from an invisible iframe embedded in the RP client application.
As defined in the specification, the page will accept post message requests from the respective RP iframe and will post back the status of the session state.
As per the specification WSO2 IS 5.2.0 will also return the additional ‘session_state’ parameter in the authentication response, when authenticated over the authorization code flow or implicit flow. This session state value is initially calculated at the server based on the salted cryptographic hash of the Client ID, Client Origin and OP Browser State.
Here Client Origin is derived from the ‘Callback Url’ registered at the OP, which is the URI scheme, hostname and port number of the ‘Callback Url’ as defined here. OP Browser State is maintained by a cookie, called ‘opbs’. The Session State value will be changed as per the end user’s authentication status (login, logout) in the browser, and as authentication status of Clients being used by the end user changes (ex: user logins for a new client) in the browser.
Once the session between the RP and OP is established with the authentication request and response, RP is expected to check the session status at the OP by polling a hidden OP iframe from a RP iframe without causing network traffic.
The post message request from the RP iframe to the OP iframe should be a concatenation of the Session State and the Client ID as below.
Client ID + “ “ + Session State
When OP iframe receives the post message it extracts the Session State and Client ID received. Then it recalculates the Session State using the received Client ID, OP browser state and Client Origin which is known. OP iframe will compare the received Session State value with the calculated value. If they match OP iframe will post the string ‘unchanged’ to the RP. if they do not match OP iframe will post as ‘changed’. If some error occurred and OP iframe could not determine the parameters needed for the calculation it will post as ‘error’.
RP iframe will receive back the post message from OP iframe, which would be either ‘changed’, ‘unchanged’ or ‘error ‘. Upon receipt of ‘changed’ RP must perform re-authentication with ‘prompt=none’ to obtain the current Session State at the OP.
If the end user has logged out from the OP, RP will receive an authentication failure along with a new Session State value. RP should handle this as a end user logout. Otherwise, RP will receive a successful authentication response along with a new Session State value and RP should update the Session State value
In order to test OIDC Session Management feature we need to have a Relying Party implementation, which implements the RP iframe. The specification itself includes an example pseudo-code for the RP iframe. You can either refer that or refer the sample ‘Playground2’ relying party application that we have updated to support OIDC session management. (Get the source code from here and build with mvn clean install. In the target folder you will see an artifact as playground2.war).
Ex:
For a fresh server running in local the endpoint URL will be as below.
https://localhost:9443/oidc/logout
By invoking this endpoint a RP can notify the OP to logout an end user from the OP. Due to some implementation limitations for the moment WSO2 IS 5.2.0 will not support the query parameters defined in the specification. Thus, a RP will not be able to request to redirect the user agent back to the RP after a logout has been performed with ‘post_logout_redirect_uri’ query parameter. But, these will be addressed soon, with the next release.
OIDC Session Iframe Endpoint:
https://<IS_SERVER_HOST>:<IS_SERVER_POST>/oidc/checksession?client_id=<CLIENT_ID>
Ex:
For a fresh server running in local the endpoint URL will be as below.
https://localhost:9443/oidc/checksession?client_id=2PX8XTppZBa6FYRZ6wqFs_o0mGYa
This endpoint provides the OP iframe, that supports session state synchronization with RP client via HTML5 postMessage API. This page should be loaded from an invisible iframe embedded in the RP client application.
As defined in the specification, the page will accept post message requests from the respective RP iframe and will post back the status of the session state.
How OIDC Session Management works at WSO2 IS 5.2.0
Here Client Origin is derived from the ‘Callback Url’ registered at the OP, which is the URI scheme, hostname and port number of the ‘Callback Url’ as defined here. OP Browser State is maintained by a cookie, called ‘opbs’. The Session State value will be changed as per the end user’s authentication status (login, logout) in the browser, and as authentication status of Clients being used by the end user changes (ex: user logins for a new client) in the browser.
Once the session between the RP and OP is established with the authentication request and response, RP is expected to check the session status at the OP by polling a hidden OP iframe from a RP iframe without causing network traffic.
The post message request from the RP iframe to the OP iframe should be a concatenation of the Session State and the Client ID as below.
Client ID + “ “ + Session State
When OP iframe receives the post message it extracts the Session State and Client ID received. Then it recalculates the Session State using the received Client ID, OP browser state and Client Origin which is known. OP iframe will compare the received Session State value with the calculated value. If they match OP iframe will post the string ‘unchanged’ to the RP. if they do not match OP iframe will post as ‘changed’. If some error occurred and OP iframe could not determine the parameters needed for the calculation it will post as ‘error’.
RP iframe will receive back the post message from OP iframe, which would be either ‘changed’, ‘unchanged’ or ‘error ‘. Upon receipt of ‘changed’ RP must perform re-authentication with ‘prompt=none’ to obtain the current Session State at the OP.
If the end user has logged out from the OP, RP will receive an authentication failure along with a new Session State value. RP should handle this as a end user logout. Otherwise, RP will receive a successful authentication response along with a new Session State value and RP should update the Session State value
How can we test OIDC Session Management with Playground2 Sample application
1. Let’s deploy two relying party applications. For that make a copy of playground2.war and rename that as playground3.war. Then deploy them both in a web server such as tomcat. Here I’m using a tomcat server running on my local machine, in port 8080.
If you invoke http://localhost:8080/playground2/ url you should see the page below. Similarly you should see the same page if you invoke http://localhost:8080/playground3/
2. Then we need to register both of these relying party applications at the WSO2 Identity Server. Here I’m running the Identity Server on my local machine.
Goto the management console and click ‘Add’ button under ‘Service Providers’ in ‘Main’ tab.
Add the ‘Service Provider Name’ as ‘playground2’ and click ‘Register’
Goto ‘Inbound Authentication Configuration’ > ‘OAuth/OpenID Connect Configuration’ and click ‘Configure’
Add “http://localhost:8080/playground2/oauth2client” to the ‘Callback Url’ and click ‘Update’
Note down the ‘OAuth Client Key’ and ‘OAuth Client Secret’ values, since we need them in order to invoke the API for authentication.
Similarly register ‘playground3’ application also with the “http://localhost:8080/playground3/oauth2client” callback url
3. Let’s try to authenticate the user via the two registered applications.
I have created a user as ‘testuser1’ at the WSO2 Identity Server and authenticating against that user.
If you invoke http://localhost:8080/playground2/ url you should see the page below. Similarly you should see the same page if you invoke http://localhost:8080/playground3/
2. Then we need to register both of these relying party applications at the WSO2 Identity Server. Here I’m running the Identity Server on my local machine.
Goto the management console and click ‘Add’ button under ‘Service Providers’ in ‘Main’ tab.
Add the ‘Service Provider Name’ as ‘playground2’ and click ‘Register’
Goto ‘Inbound Authentication Configuration’ > ‘OAuth/OpenID Connect Configuration’ and click ‘Configure’
Add “http://localhost:8080/playground2/oauth2client” to the ‘Callback Url’ and click ‘Update’
Note down the ‘OAuth Client Key’ and ‘OAuth Client Secret’ values, since we need them in order to invoke the API for authentication.
Similarly register ‘playground3’ application also with the “http://localhost:8080/playground3/oauth2client” callback url
3. Let’s try to authenticate the user via the two registered applications.
I have created a user as ‘testuser1’ at the WSO2 Identity Server and authenticating against that user.
Invoke “http://localhost:8080/playground2/ “ and click on ‘Import Photos’
Provide below inputs and click ‘Authorize’
Authorization Grant Type :
Authorization Code (With this sample we can test OIDC only for Authorization Code flow)
Client Id :
Client ID of the playground2 application registered
Scope :
openid
Callback URL :
http://localhost:8080/playground2/oauth2client
Authorize Endpoint :
https://localhost:9443/oauth2/authorize
Logout Endpoint :
https://localhost:9443/oidc/logout
Session Iframe Endpoint :
https://localhost:9443/oidc/checksession?client_id=<ClientID of playground2 application>
Login page will prompt. Provide the username and password.
Click ‘Approve Always’ in the consent page
Once the authentication is success, OP will redirect back to the client application with the authorization code and the session state.
If you see the console you will see the console logs printed by the RP iframe while polling the OP iframe.
Get the ID token and access token by giving inputs below.
Authorization Code :
Code received
Callback URL :
http://localhost:8080/playground2/oauth2client
Access Token Endpoint :
https://localhost:9443/oauth2/token
Client Secret :
Client Secret of playground2 application
OP will respond with the ID token and access token and you will see the below page.
We can further invoke the user info endpoint and obtain user claims with the access token received.
Give the ‘UserInfo Endpoint’ as ‘https://localhost:9443/oauth2/userinfo?schema=openid’ and click ‘Get UserInfo’
Similarly, invoke the playground3 application.
As soon as you receive the authorization code for the initiated authentication request for playground3 app, if you open up the browser console of playground2 application, you will note that RP iframe of playground2 has initiated a passive authentication request as the session state has been changed. And as the response has received, app will update it’s session state value and keep polling the OP iframe again.
Go back to the playground3 application and click ‘Logout’. Then approve the logout consent prompted.
Now the user will be logged out from the OP.
Go back to the playground2 application. You will note that the home page has been loaded. If you see the console you will note that the playground2 app’s RP iframe has initiated a passive authentication request and has received an error since end user session is no longer available at OP. So the app has handled this as a logout scenario.