OAuth2 and OpenID Connect with WSO2 IS - Part 13

Photo by Jye B on Unsplash

OAuth2 and OpenID Connect with WSO2 IS - Part 13

Session Management and Logout

🧬 Introduction

The OIDC specification defines methodologies to manage user sessions and log out the end-users at the authorization server using front-channel communication. In this approach, the login/logout requests from the client application(RP or relying party) to the OpenID Provider(OP) and OP to RP are done using the user agent(browser).

To get the user's logging data, the specification recommends an approach to poll a hidden OP iframe from an RP iframe with an origin-restricted post message. The main advantage of this is it will not cause any network traffic.

  • OP iframe

    ↳ This loads on the RP side based on the OP's check_session_iframe endpoint. The OP iframe must enforce that the caller has the same origin as its parent frame. It must reject post-message requests from any other source origin.

  • RP iframe

    ↳ This iframe loads the RP side and it should know the ID of the OP iframe. This iframe can post-message to the OP iframe. RP iframe should continuously post messages(poll) to the OP iframe at an interval as per the application requirements.

  • Session State

    ↳ This is a JSON string that represents the end-user's login state at the OP. The session_state value contains a salted cryptographic hash of the Client ID, origin URL, and OP browser state. The OP passes this value to the RP in the authentication response and the RP uses this value to monitor the end-user session at the OP.

The following diagram represents how the OIDC session management works.

  • Once the end-user needs to log in to the RP, the RP sends an authentication request to the OP.

  • The OP responds with session_state

  • The RP iframe continuously polls the OP iframe to detect any state changes.

  • The OP iframe responds with one of the following statuses.

    • unchanged

      ↳ This indicates that the user session is still valid at the OP. RP will continue to poll OP iframe to detect any session changes.

    • changed

      ↳ This indicates that the session has changed at the OP. This can happen due to user logout, session timeout, or a user logging in from a different client application. Upon receipt of the changed state, the RP performs re-authentication with prompt=none to obtain the current session state at the OP.

    • error

      ↳ This indicates the message sent was determined by the OP to be malformed. Upon receipt of error, the RP must not perform re-authentication with prompt=none, to not cause potential infinite loops that generate network traffic to the OP, it directly logs the user out.

This is how session management works in OIDC's context. In OIDC, there are 4 specs related to OIDC logout.

  • OIDC Session Management Logout

    ↳ This is what we discussed. This defines how to monitor the end-user's login status at the OP.

  • OIDC RP-Initiated Logout

    ↳ This spec defines how an application should invoke a logout request to an OP.

  • OIDC Front-channel Logout

    ↳ This and OIDC Session Management specification use front-channel communication, which communicates logout requests from the OP to RPs via user-agent.

  • OIDC Back-channel Logout

    ↳ This specification uses direct back-channel communication between the OP and RPs being logged out.

Simply we can say that RP-initiated logout is the way to invoke OIDC logout at the IDP side by an RP. But OIDC Session Management, OIDC front-channel logout, and OIDC back-channel logout are logout notification mechanisms. Once the IDP’s session is terminated by RP-initiated logout, the logout notifications will be sent to all RPs in the same browser session. Those notification mechanisms can vary depending on OIDC Session Management, OIDC front-channel logout, and OIDC back-channel logout.

🧪 OIDC Session Management Logout with WSO2 IS

This is what we have discussed in the Introduction part of this article. In this section, we will be looking at how you can work with session management in WSO2 IS. First, let's create a new SP using the DCR endpoint.

curl -k -X POST -H "Authorization: Basic YWRtaW46YWRtaW4=" -H "Content-Type: application/json" -d '{"client_name": "playground_2","grant_types": ["authorization_code","password"], "redirect_uris": ["http://localhost:8080/playground2/oauth2client"],"ext_param_client_id":"provided_client_id0001","ext_param_client_secret":"provided_client_secret0001" }' "https://localhost:9443/api/identity/oauth2/dcr/v1.1/register"

You need to have the playground2 application downloaded and put into apache-tomcat's webapps directory to test this scenario. You can check my article on Authorization Code Grant type to check how you can configure the playground2 application.

Next, create another SP with the following command. You need to configure the playground2.properties file considering the Redirect URI, Client ID, and Client Secret defined there. This app will be playground3.

curl -k -X POST -H "Authorization: Basic YWRtaW46YWRtaW4=" -H "Content-Type: application/json" -d '{"client_name": "playground_3","grant_types": ["authorization_code","implicit","password"], "redirect_uris": ["http://localhost:8080/playground3/oauth2client"],"ext_param_client_id":"provided_client_id0002","ext_param_client_secret":"provided_client_secret0002" }' "https://localhost:9443/api/identity/oauth2/dcr/v1.1/register"

Next, open http://localhost:8080/playground2/oauth2.jsp to view the configurations of playground2 application.

  • Client ID

    ↳ This is the Client ID of the playground2 application. In my case it is, provided_client_id0001

  • 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

    ↳This should be https://localhost:9443/oidc/checksession?client_id=provided_client_id0001 with client_id replaced with your playground2 app's Client ID.

After you click on Authorize you will be prompted to enter the username and password. Use the default username(admin) and password(admin) for this.

If you are prompted to a consent page, click on Approve Always to continue. After that, you will get the Authorization Code as a URL parameter and get another parameter named, session_state This parameter is calculated separately for each RP. It is the combination of Client ID, Origin URL(Callback URL), and the opbs cookie value. Therefore, it is unique to each RP.(session_state = hash(client_id + opbs cookie value + origin url) )

In my case,

  • Client ID

    provided_client_id0001

  • opbs Cookie Value

    7d700ebd-6e33-42f1-9a93-8cc638e8e7c6

  • session_state

    98375abaae0fde1ba00cb7ce8b33903ab3e58c6ff55662320c8cabc3a9586c85.2wLh93tiWpnqat_TyW0qVg

The RP iframe sends a post message to the OP iframe with https://localhost:9443/oidc/checksession?client_id=provided_client_id0001 in this case. This contains the Client ID, session_state and the opbs cookie value. This will be done periodically by RP (polling). Then OP iframe re-calculates the session_state using the Client ID and the opbs cookie value. (session_state_recalculated = hash(client_id + opbs cookie value + origin url))

Then it will check whether the recalculated session_state and the session_state sent by the RP are the same or not. (session_state_recalculated == session_state_sent_by_RP)

Now let's try to log in with playground3 app in the same browser session. In this case, you'll notice that a new value will be set for opbs cookie. Then, for the playground2 application the session_state values will be,

  • Client ID

    provided_client_id0001

  • opbs Cookie Value

    fe6ededa-f689-43c8-a0b0-64273fb722cc

  • session_state

    98375abaae0fde1ba00cb7ce8b33903ab3e58c6ff55662320c8cabc3a9586c85.2wLh93tiWpnqat_TyW0qVg

With this, the session_state sent to the OP iframe will not equal the session_state calculated by the OP iframe. Therefore, the OP will send a message as changed.

As playground2 application received the message as changed, it will try to authenticate again with the following request.

https://localhost:9443/oauth2/authorize?client_id=provided_client_id0001&scope=openid&response_type=code&redirect_uri=http://localhost:8080/playground2/oauth2client&prompt=none

The most important parameter here is, prompt=none. If prompt=none then the Authorization Server will not display any authentication or consent user interface pages. This is why we need to select Approve Always at the initial consent screen when we first login to the playground2 application. If the user is logged out, it will return an error. Otherwise, it will not return any error messages.

If the playground3 application invokes the /logout API, WSO2 IS will clear the session and the opbs cookie will be removed. Then, the session_state of the playground2 will be changed. At that time, if the https://localhost:9443/oauth2/authorize?client_id=provided_client_id0001&scope=openid&response_type=code&redirect_uri=http://localhost:8080/playground2/oauth2client&prompt=none is sent, the re-authentication will fail since the user is logged out. Then you will get a response like this in the URL.

http://localhost:8080/playground2/oauth2client?error_description=Authentication+required&error=login_required

This is how the Session Management Logout works in WSO2 IS. Next, we will look at the RP-Initiated Logout.

🧪 RP-Initiated Logout with WSO2 IS

RP-initiated logout specification defines how an application should invoke a logout request to an OP. RP will invoke the logout endpoint of the OP via the user agent. That logout request may contain the following query parameters.

  • id_token_hint

    ↳ This is the previously issued ID Token. This should be passed to the logout endpoint as a hint about the end user's current authenticated session with the client. This is used as an indication of the identity of the end-user that the RP is requesting to be logged out by the OP. The OP need not to be listed as an audience of the ID Token(aud) when it is used as an id_token_hint value. This can be used instead of the client_id parameter.

  • client_id

    ↳ This is the Client ID obtained when registering the application in WSO2 IS. To use client_id as a logout parameter you have to add the following configuration in <IS_HOME>/repository/conf/deployment.toml under [oauth.oidc.logout_params], use_client_id=true

  • post_logout_redirect_uri

    ↳ This is the URL to which the RP is requesting that the end user's user agent be redirected after a logout has been performed.

  • state

    ↳ An opaque value used by the RP to maintain the state between the logout request and the callback to the endpoint specified by the post_logout_redirect_uri query parameter. If included in the logout request, the OP passes this value back to the RP using the state query parameter when redirecting the user-agent back to the RP.

According to the specification,

  • When an OP receives a logout request, it will remove the end user's session in the OP. This process is called RP-initiated logout.

  • When an id_token_hint parameter is present, OP must validate that it was the issuer of the ID token.

  • At the logout endpoint, the OP should ask the end-user whether to log out of the OP as well. If the end-user says "yes", then the OP must log out the end-user.

  • As part of the OP logging out the end-user, the OP uses the logout mechanism(s) registered by the RPs to notify any RPs logged in as that end-user that they are to likewise log out the end-user

  • Then OP will notify other RPs in the same browser session about this logout event. How IDP does that depends on the logout mechanism (it can be back-channeled, front-channeled, or session management logout). The OP should use the logout mechanism(s) registered by the RPs to notify any RPs logged in as that end-user that they are to likewise log out the end-user.

Check the following diagram to understand more about how WSO2 IS uses the RP-initiated logout.

  1. RP has to invoke the OIDC logout endpoint.

  2. WSO2 IS validates the logout request.

  3. WSO2 IS prompts for consent.

  4. WSO2 IS terminates the IDP session.

  5. WSO2 IS redirects to the post_logout_redirect_uri

Let's take the playground2 application and log in using the implicit grant type to obtain an Access Token and an ID Token first.

https://localhost:9443/oauth2/authorize?response_type=id_token token&client_id=provided_client_id0001&scope=openid&redirect_uri=http://localhost:8080/playground2/oauth2client&nonce=oidc

It will give a result like this in the URL after the authentication.

http://localhost:8080/playground2/oauth2client#access_token=606891d1-c089-3081-b7b3-91e7d61a6c5e&scope=openid&id_token=eyJ4NXQiOiJNREpsTmpJeE4yRTFPR1psT0dWbU1HUXhPVEZsTXpCbU5tRmpaalEwWTJZd09HWTBOMkkwWXpFNFl6WmpOalJoWW1SbU1tUTBPRGRpTkRoak1HRXdNQSIsImtpZCI6Ik1ESmxOakl4TjJFMU9HWmxPR1ZtTUdReE9URmxNekJtTm1GalpqUTBZMll3T0dZME4ySTBZekU0WXpaak5qUmhZbVJtTW1RME9EZGlORGhqTUdFd01BX1JTMjU2IiwiYWxnIjoiUlMyNTYifQ.eyJpc2siOiJkNWM5YWI2YTFhNGFiMmJmZjNlOWZiMDI2YjYxZGU1ZTk4NDBhODNjZjJjM2UxZDk3MzBkYjdiYmI5ODA4NjNjIiwiYXRfaGFzaCI6Inc2elM5VjRxWVR0dFRTNjQ2ZXNIaEEiLCJhdWQiOiJwcm92aWRlZF9jbGllbnRfaWQwMDAxIiwic3ViIjoiNzhlZmQzMzQtZWM2YS00OTc1LWFkMWUtZWZmNjhlNTY5ZTRiIiwiYXpwIjoicHJvdmlkZWRfY2xpZW50X2lkMDAwMSIsImFtciI6WyJCYXNpY0F1dGhlbnRpY2F0b3IiXSwiaXNzIjoiaHR0cHM6XC9cL2xvY2FsaG9zdDo5NDQzXC9vYXV0aDJcL3Rva2VuIiwiZXhwIjoxNzEwMTU1MjI3LCJpYXQiOjE3MTAxNTE2MjcsIm5vbmNlIjoib2lkYyIsImp0aSI6ImZkNjA0ZTE1LTMyZWQtNGFiNi04MzQzLTVlZTM5MDY2YjU1OCIsInNpZCI6IjhiNWYyNGZmLTJlYTktNDlmZS04ZDdmLTBkNDQyMDhiN2UwZiJ9.KmSZTiOxkHQKDKEqnVoAUPcN9v3LXm382J2_oF_xn8PkUg2bSON98rmC4XL0kPo-4oaHmtGgzcljX69-q0zzYrQesK2L1My3qsIQU6b9G1jHXN3u9DCIQvzpXUKEGymQavUrbaFNBsOB_OykeuD6gkvCbYqU4i2veW-9J01k2P8lKDmdAJnEpPoMv6Cml1J-3tmp4jrZbumLfqFqwi04Ufg6OIPy0U__u5eXNpNhi_8guH8SHyQHtWTo4JTFpJfVA345E3MBahuQbYnEufpY30YqNNMkuFeVViixK_OdV6b8Oe7RSuFsOuIb-mw0T47KzoKJvkV8g1e5j4sJfKCqEQ&token_type=Bearer&expires_in=3599&session_state=ee0a701abfd70e33cd4c4b61a88f200b37eefd6fe19bd1f016d9f864d0b1c5bc.-Dtvgl36Q5_Y15e1ZXOstQ

Now, we can send a request to the logout endpoint of WSO2 IS, which is https://localhost:9443/oidc/logout The RP-initiated logout request will have post_logout_url and id_token_hint parameters as well.

https://localhost:9443/oidc/logout?post_logout_redirect_uri=http://localhost:8080/playground2/oauth2client&id_token_hint=eyJ4NXQiOiJNREpsTmpJeE4yRTFPR1psT0dWbU1HUXhPVEZsTXpCbU5tRmpaalEwWTJZd09HWTBOMkkwWXpFNFl6WmpOalJoWW1SbU1tUTBPRGRpTkRoak1HRXdNQSIsImtpZCI6Ik1ESmxOakl4TjJFMU9HWmxPR1ZtTUdReE9URmxNekJtTm1GalpqUTBZMll3T0dZME4ySTBZekU0WXpaak5qUmhZbVJtTW1RME9EZGlORGhqTUdFd01BX1JTMjU2IiwiYWxnIjoiUlMyNTYifQ.eyJpc2siOiJkNWM5YWI2YTFhNGFiMmJmZjNlOWZiMDI2YjYxZGU1ZTk4NDBhODNjZjJjM2UxZDk3MzBkYjdiYmI5ODA4NjNjIiwiYXRfaGFzaCI6Inc2elM5VjRxWVR0dFRTNjQ2ZXNIaEEiLCJhdWQiOiJwcm92aWRlZF9jbGllbnRfaWQwMDAxIiwic3ViIjoiNzhlZmQzMzQtZWM2YS00OTc1LWFkMWUtZWZmNjhlNTY5ZTRiIiwiYXpwIjoicHJvdmlkZWRfY2xpZW50X2lkMDAwMSIsImFtciI6WyJCYXNpY0F1dGhlbnRpY2F0b3IiXSwiaXNzIjoiaHR0cHM6XC9cL2xvY2FsaG9zdDo5NDQzXC9vYXV0aDJcL3Rva2VuIiwiZXhwIjoxNzEwMTU1MjI3LCJpYXQiOjE3MTAxNTE2MjcsIm5vbmNlIjoib2lkYyIsImp0aSI6ImZkNjA0ZTE1LTMyZWQtNGFiNi04MzQzLTVlZTM5MDY2YjU1OCIsInNpZCI6IjhiNWYyNGZmLTJlYTktNDlmZS04ZDdmLTBkNDQyMDhiN2UwZiJ9.KmSZTiOxkHQKDKEqnVoAUPcN9v3LXm382J2_oF_xn8PkUg2bSON98rmC4XL0kPo-4oaHmtGgzcljX69-q0zzYrQesK2L1My3qsIQU6b9G1jHXN3u9DCIQvzpXUKEGymQavUrbaFNBsOB_OykeuD6gkvCbYqU4i2veW-9J01k2P8lKDmdAJnEpPoMv6Cml1J-3tmp4jrZbumLfqFqwi04Ufg6OIPy0U__u5eXNpNhi_8guH8SHyQHtWTo4JTFpJfVA345E3MBahuQbYnEufpY30YqNNMkuFeVViixK_OdV6b8Oe7RSuFsOuIb-mw0T47KzoKJvkV8g1e5j4sJfKCqEQ&state=statexyz

If you enter the above URL in a new tab, you will be prompted to give consent for logging out.

The validation of id_token_hint in the above URL happens because the Client ID is sent as a claim value within the ID Token. And the post_logout_redirect_uri is also validated because of the id_token_hint value.

If the user provides consent, the WSO2 IS will clean the user's SSO session and remove all the session-related cookies such as commonAuthId and opbs cookies.

🧪 OIDC Back Channel Logout with WSO2 IS

Let's go to the created playground2 application and add the Logout URL there. If you configure this, the logout token will be sent there.

  • Logout URL

    https://localhost:8080/playground2/bclogout

Now, log in to the application with the Implicit Grant type,

https://localhost:9443/oauth2/authorize?response_type=id_token token&client_id=provided_client_id0001&scope=openid&redirect_uri=http://localhost:8080/playground2/oauth2client&nonce=oidc

Select the following and click on Authorize.

  • Authorization Grant Type

    ↳ Implicit

  • Client Id

    ↳ Client ID of the playground2 application, in my case it is, provided_client_id0001

  • Scope

    ↳ opened

  • Implicit Response Type

    ↳ ID token and access token

  • Callback URL

    http://localhost:8080/playground2/oauth2client

Now, if you click the Logout button you will be prompted to select your consent.

If you have given consent to log out, you will be logged out.

Currently, the front channel logout is not implemented in WSO2 IS. So this is it about OIDC Session Management and Logout. We will look at how you can use PKCE for the Authorization Code Grant in the next article.

📚 References