B2C Identity Experience Framework Policy Custom User Journey not followed when using refresh token flow

Kholofelo 66 Reputation points


First some context
We are using IEF custom policies with a B2C to allow for extra claims to be returned from a function app. E.g. the extra claim states whether the user is suspended or not.

This flow works well when doing a normal sign in either through the login UI or via an active access token. This means we can effectively change a user from being active to being suspended in the backend and it will reflect in their id token claims next time they sign in.

The client application inspects the custom claim to check if it should allow the user to perform certain functions or not.

below is an example of the claims

    "iss": "https://{tenant}.b2clogin.com/xxxx/v2.0/",
    "exp": 1613592171,
    "nbf": 1613588571,
    "aud": "xxxxx",
    "sub": "xxxxx",
    "name": "Kholofelo Moyaba",
    "given_name": "Kholofelo",
    "family_name": "Moyaba",
    "custom_claim": "suspended",
    "tid": "xxxx",
    "nonce": "xxxx",
    "scp": "read",
    "azp": "xxxx",
    "ver": "1.0",
    "iat": 1613588571

The client application used uses a refresh token flow to avoid having to log in with the password most of the time

The issue

When a user is logged in via the refresh token flow, for some reason the custom claims provider from the custom IEF policy is not invoked. This means we don't get the user's updated custom claim. It seems somehow the new token returned contains the old custom claim.

This effectively means a user can be banned but the client application is not aware of that if the refresh token is used - the only way for the claim to be reflected is to log out and then log in again.

How can we allow the custom claims provider to be invoked when using refresh token flow?
Failing to do so we will need to add extra code to the client application to allow for querying the suspended/active status of the user directly as opposed to relying on the claims returned.

Microsoft Entra External ID
Microsoft Entra External ID
A modern identity solution for securing access to customer, citizen and partner-facing apps and services. It is the converged platform of Azure AD External Identities B2B and B2C. Replaces Azure Active Directory External Identities.
2,722 questions
0 comments No comments
{count} votes

Accepted answer
  1. soumi-MSFT 11,756 Reputation points Microsoft Employee

    Hello @Kholofelo , thank you for reaching out. It's not that the claim isn't returned, it's that it doesn't contain updated information from the Function App

    To elaborate a little more in this case there are three entities involved: Function App (with information on the user and their password), B2C, and App. The user accessing the app after already validating credentials with the B2C and getting the token from B2C. Next, the application code sees the access token from B2C is going to expire.
    In order to keep authentication, the application refreshes its token using a refresh token. But because we never go to the Function App which during the refresh time contains new information on the user, B2C won't get the updated information until they log in again. If that information was instead written to the B2C profile before refresh time, I am sure it would show in the new access token.

    This is because we don't need to validate the user's identity with the Function App during refresh time because we've already done that during initial auth.

    Now a fix that I can think off would work here, is adding the writing the claim value into the B2C user profile. You can use Microsoft Graph API and the user PATCH HTTP method to update the user profile. You can find more information here: https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-custom-attributes?pivots=b2c-custom-policy#using-custom-attribute-with-ms-graph-api

    Hope this helps.

    Do let us know if this helps and if there are any more queries around this, please do let us know so that we can help you further. Also, please do not forget to accept the response as an Answer; if the above response helped in answering your query.

0 additional answers

Sort by: Most helpful