Microsoft Azure AD B2C and refresh tokens for Single Page Applications

13 November 2020
Author: Mikko Vuorinen, Senior Developer

Browser-based single page applications (SPA) saw a surge in popularity in the 2010s. To keep up with the changing scenery, best security practices have evolved and keep evolving, together with support from browser vendors. For authentication and authorization solutions, it is essential to keep up with these changes to maintain and improve security without sacrificing user experience.

This article highlights some of the recent changes, and describes how application developers can utilise B2C to get the most out of modern browser and B2C capabilities.

Refresh tokens

You should not use refresh tokens in a SPA, right? Browser applications can use session cookies to silently get a new token, right? Not quite, and eventually – when the browser vendors implement their plans to block third party cookies – not at all. Safari and Firefox already do this, and Chromium-based Google Chrome and Edge are soon to follow.
Refresh tokens can be used in a browser with the same level of security as cookies, assuming that two key security measures are taken by the client application and the authorization server. First, tokens must be retrieved using a background POST request instead of a parameter in the redirect URI (i.e. Implicit flow). Second, refresh tokens must be rotated after each use and must expire if not used.

Proof Key for Code Exchange (PKCE)

PKCE is an extension to the OAuth authorization code flow. In practice, it is an alternative (or addition) to the use of client secrets. For browser applications, it gives the necessary tools to implement the first key measure that allows the use of refresh tokens: getting the token via a background POST request. Most modern OAuth / OIDC client libraries implement PKCE so that it can be enabled in applications without additional code.

Sample

The following sample shows how the combination of PKCE and refresh tokens can be used to allow the application to use a short-living access token and refresh it in the background using a refresh token.

Registering SPA in B2C

To use the sample code below, you will need to register an application in Azure AD B2C. When registering the application, use the Single Page Application (SPA) type redirect URI. This enables PKCE and refresh token support for browser applications.

Generate code verifier and challenge

The first step of the PKCE flow is to generate a secret code verifier. Then the code challenge is calculated based on the verifier using SHA256 hashing algorithm.
Below is an example with values you can use to initiate the authentication flow:
# Generated code verifier
@codeVerifier = 1qaz2wsx3edc4rfv5tgb6yhn1234567890qwertyuiop

# Code challenge calculated as Base64-UrlEncode(SHA256(@codeVerifier))
@codeChallenge = _r67lcj4MoDNBAkhxS7ke_YKhKCBAiM0SgzNCagbCxo

Authorize to get auth code

The authorization call is similar to the normal auth code flow, with the additional code_challenge and code_challenge_method parameters. (Note: authUrl is split into multiple lines for readability).

# B2C configuration
@tenant =
@clientId = 00000000-0000-0000-0000-000000000000
@policy = b2c_1_signinsignup

@authUrl = https://{{tenant}}.b2clogin.com/{{tenant}}.onmicrosoft.com
/{{policy}}/oauth2/authorize
?client_id={{clientId}}
&scope=openid
&response_type=code
&response_mode=fragment
&redirect_uri=https://jwt.ms
&nonce=MyNonce
&code_challenge={{codeChallenge}}
&code_challenge_method=S256

You can copy paste the authUrl into a browser. Once signed in, the browser will be redirected to https://jwt.ms with a code parameter in the URL. Copy that parameter value and continue to the next step.

Get tokens using auth code

The token call is also similar to the normal auth code flow, but with the additional code_verifier parameter. Passing the verifier allows the authorization server to check that the token call is from the same caller as the authorization call.

# Copy the code from previous step here
@code = ey...

POST https://{{tenant}}.b2clogin.com/{{tenant}}.onmicrosoft.com/{{policy}}/oauth2/token
Content-type: application/x-www-form-urlencoded

grant_type=authorization_code
&code={{code}}
&client_id={{clientId}}
&code_verifier={{codeVerifier}}
&redirect_uri=https://jwt.ms

If the call is successful, it will return a JSON object that looks like this:

{
"access_token": "ey...",
"id_token": "ey...",
"token_type": "Bearer",
"not_before": 1602078559,
"expires_in": 3600,
"expires_on": 1602082159,
"resource": "https://jwt.ms",
"refresh_token": "ey...",
"refresh_token_expires_in": 1209600
}

We are mostly interested in the refresh_token property. Although it looks like a base64 encoded token like the access and id tokens, it is somewhat different. There is no specific format for the refresh token as it will only need to be understood by the authorization server.

If you have used refresh tokens before, you might notice that there is no offline_access specified in the request. With the new SPA application type in B2C, you should not use the offline_access scope. Instead, browser applications will automatically get issued a refresh token which has a shorter lifetime.

B2C also provides a property refresh_token_expires_in, but this is outside the OAuth standard. A client application can only check if the refresh token is valid by trying to use it. If authentication with the refresh token fails, the user will need to reauthenticate. The expiry in the example is 14 days, but B2C will most likely change it to 24 hours for SPA’s.

Get new tokens using refresh token

Once you have the refresh token, you can use it to get a new access token when needed. This is similar to the token call above, but with a grant type refresh_token.
# Copy the refresh token from previous step here
@refreshToken = ey...

POST https://{{tenant}}.b2clogin.com/{{tenant}}.onmicrosoft.com/{{policy}}/oauth2/token
Content-type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token={{refreshToken}}
&client_id={{clientId}}
&redirect_uri=https://jwt.ms

This should result in a similar response as the original token request, with a new access and id token as well as a new refresh token. The new refresh token can be used the same way as the original one.
As mentioned earlier, the authorization server must enforce refresh token rotation to ensure that refresh tokens are secure in browser applications. This means that once a refresh token has been used, the same token cannot be used again, and the application must use the new refresh token instead.

Wrap-up

Modern browsers are constantly evolving to improve the privacy and security of their users. PKCE and refresh tokens are the next step forward in securing single-page applications that use OAuth and OpenID Connect. This is also the case when using federated authentication providers such as Azure AD B2C.

I hope you found this article a useful introduction to the topic.