PAUL'S BLOG

Learn. Build. Share. Repeat.

Azure AD B2C logouts and redirection URLs

2022-06-18 6 min read Tutorial

Background

I’ve been helping a client build a customer-facing NodeJS web application which leveraged Azure AD B2C as its identity provider. Things were going well with the development and Azure AD B2C served them really well. It’s cost-effective and gives them all the controls and security features they’ve come to expect with Azure AD (the non-B2C version). As any responsible company, they run penetration tests on the application prior to releasing to production and they identified one item that can pose as a security threat.

As part of the security audit, they noticed upon logging out of the application the post_logout_redirect_uri parameter in the URL controlled where the user is “dropped off” after logging out. This became an area of concern. The concern here is that if the application gets compromised, a malicious actor can inject an unauthorized url into the request object and take the user to a “spoofed” login page and ultimately collect login credentials unknowingly from the user. This sort of attack can be categorized as an Input Capture technique within the MITRE ATT&CK framework. There are a few stack overflow posts that discusses this topic of an open redirect.

An argument made that if someone were to be able to modify the URL from within the app, there are bigger security concerns than what we are facing here. Nonetheless, it will be a good idea to implement some security measures using Azure AD B2C to ensure users are only redirected to “approved” URLs. So lets dive in.

Solution

This doc states the following:

After logout, the user is redirected to the URI specified in the post_logout_redirect_uri parameter, regardless of the reply URLs that have been specified for the application. However, if a valid id_token_hint is passed, and the Require ID Token in logout requests is turned on, Azure AD B2C verifies that the value of post_logout_redirect_uri matches one of the application’s configured redirect URIs before performing the redirect. If no matching reply URL was configured for the application, an error message is displayed and the user is not redirected.

To set the required ID Token in logout requests, see Configure session behavior in Azure Active Directory B2C.

So there are two things you need to do:

  1. Configure your sign-up/sign-in user flow to require an id_token_hint on logout
  2. Pass in the id_token_hint on logout

For this walkthrough, I’ve setup a basic Azure AD B2C tenant and configured an “out-of-the-box” sign-up and sign-in user flow.

If you don’t have an Azure AD B2C tenant yet and want to follow along, go through this step to set up your tenant, then this step to create a new app registration, and finally this step to configure a sign-up and sign-in user flow and come back.

The application registration details will be used in our sample JavaScript application in a section below.

Azure AD B2C configuration

To configure your Azure AD B2C user flow:

  • Open your Azure AD B2C resource in the B2C tenant

  • Click on User flows in the left nav

    User flows

  • Click on your sign-up/sign-in user flow from the list of user flows

    Sign-up and sign-in

  • Click on Properties

    Sign-up and sign-in properties

  • Scroll down to Session behavior and set the “Require ID Token in logout requests” radio button to Yes

    Require id_tokens on logout requests

That’s it from the Azure AD side. Easy, right?!?

Add id_token to your JavaScript application

To configure your app to retrieve the id_token and send the id_token on logout request

  • Clone this sample application then open it using VSCode

    git clone https://github.com/Azure-Samples/ms-identity-b2c-javascript-spa.git
    
  • Since we’ll be using a JavaScript application to test with, make sure you update your application registration and add a “Single-page application” as a platform and update the Redirect URI to include the JavaScript app’s URL (http://localhost:6420). This is the URL that Azure AD will use to validate against when logging a user out.

    ~App redirect URI

  • Update apiConfig.js with your Azure AD B2C tenant name

    apiConfig.js

  • Update authConfig.js with your Azure AD B2C app registration client id

    authConfig.js

  • Update policies.js file with your Azure AD B2C tenant name and policy names

    policies.js

  • Update the authRedirect.js file

    • add a new global variable called id_token:

      id_token

    • in the handleResponse method, set the id_token

      handleResponse

    • in the logoutRequest, pass in the idTokenHint

      logoutRequest

So, what we did here is configure our application to point to our Azure AD B2C tenant and application registration. Then we updated some of the MSAL.js code to retrieve include a new id_token variable, store it, and use it on the logout request.

Now, the application will send the id_token as part of the logout request and this satisfies the requirement we introduced by setting the property in our “sign-up/sign-in” user flow.

Let’s test it now.

Test the solution

To test, let’s run the app:

  • Run npm start

  • Open a browser window and navigate to http://localhost:6420

  • Click the Sign-in button

  • Open F12 browser tools

  • Click the Network tab

  • Check the Preserve log box

  • Click the Sign out button

  • In the F12 tools, click on the logout URL request and copy the Request URL

  • Using a text editor, modify the URL and set the post_logout_redirect to a website that is not included in your app registration’s list of redirect URIs. Here is my sample URL.

    https://securedog.b2clogin.com/securedog.onmicrosoft.com/b2c_1_susi/oauth2/v2.0/logout?post_logout_redirect_uri=https://www.google.com&client-request-id=99f11b24-b3dd-4ab0-b7e7-e3fe6fac2854&id_token_hint=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1jNTdkTzZRR1RWQndhTmsifQ.eyJleHAiOjE2NTMyNjk0NTgsIm5iZiI6MTY1MzI2NTg1OCwidmVyIjoiMS4wIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmVkb2cuYjJjbG9naW4uY29tLzBhODk1MTgxLWEyNzYtNDRjOC04NDlkLTI1ODFlN2RiNGJhYS92Mi4wLyIsInN1YiI6ImM2NmYxMDQ4LTEyZDQtNDVlMy1iMzIwLTI3M2Q1ZGVhNjVjYyIsImF1ZCI6IjI2MzM5NzkzLTIyZjItNDI3ZS05NmEzLWZlMTkwMDlhODRlZSIsIm5vbmNlIjoiMmQxZmRkZWItNTc1ZS00NTRlLTgzMTQtYzM0NjdlZDM0ZWEzIiwiaWF0IjoxNjUzMjY1ODU4LCJhdXRoX3RpbWUiOjE2NTMyNjU4NTcsIm9pZCI6ImM2NmYxMDQ4LTEyZDQtNDVlMy1iMzIwLTI3M2Q1ZGVhNjVjYyIsIm5hbWUiOiJQYXVsIiwidGZwIjoiQjJDXzFfc3VzaSJ9.KSaRAfIxnICglfg9Bm04XElKjdV5SC4RTVD-BBDueuaqMj5PJM7RRnPZjUgqyVPBSRDdqI90fHSuGid-xmKe3t8bNhhvg-zfkvfuyIrc7fa1e7u5j7QYMkn7V6zcojOo92s6pNZ0aPVKz0WaKAG2yq0sMWLtZtvm4v2vbQn9T3Hzv-F4FpK-9blOvqvVnbk7_hPreouSTykTICMIdEmnSEXt1d0ttXHHyXX9A2ieoRtCTuZe7sJjYPMpw9gJoYum4jTvCu2agPA8yu7K4WkElErUcN7tUmm-VrEdcuoJSg-Y6HCr6WLN4KZOuAVobPZlrsiX0ZmA7DRzkORwjtHWyA

    Here we are attempting to redirect the user to https://google.com which is NOT included in our list of application redirect URIs

  • You should be signed out, click the Sign-in button again

  • Now, paste in the modified URL into a new browser tab

  • You should see the following image

    logout-error

  • If your requested redirect URI is not in the list of Redirect URIs then the logout attempt will fail

Summary

To recap, in order to enforce the validation of the post_logout_redirect_uri parameter value against what you have configured in your application registration’s redirect URIs, you must set your user flow to require an id_token on logout then update your application to pass store the id_token and pass it back when making a request to log out.

I only walked through updating the code in the authRedirect.js file which is meant to redirect users in place during the authentication process. If you’d like to use pop-up pages instead for user authentication, you can go ahead and update the authPopup.js file in the same manner as we did above.

This should keep your security team happy.

Additional resources