12

I am using Azure AD B2C to authenticate users of my mobile app. I use Microsoft.Identity.Client on the device AcquireTokenSilent and AcquireTokenInteractive to manage user authentication state. Everything works great.

When the user has completed authentication, my server will check their ID to see if they're registered in my user database, and if not will ask for their details and save them. At this point I have a local unique Id for the user which I would like to add to their access token so that other services can use it for authorization. So I call the AD B2C graph API to set a UserId custom attribute. I have registered the custom attribute with Azure AD B2C and my User Flows all have UserId selected under Application Claims so that it gets added to the JWT. I am basically following option 1 described here: Add claims into token Azure B2C. The configuration appears to be correct and everything works perfectly...

... except for one thing. The UserId claim only appears in the Access Token after the user logs out and logs in again.

So after the user authenticates, I register him in my DB, call the Graph API to set his UserId claim. Thereafter any time I acquire a token on the device, even if I use WithForceRefresh(true), I get a token without the UserId claim. If I call the Graph API I can see that the claim exists on the user's profile, but it doesn't make it to the token. As soon as the user logs out and in again, He gets a token that includes the claim.

To work around this I either need to force the user to log in again after he has registered, which would be silly. Or I need to call the Graph API and fill in the missing information if it's not present in the token, which would also be sub-optimal.

What am I missing here? What is the right way to force AD to refresh the user's access token with new claims without requiring the user to log in again?

UPDATE I used the example of a UserId for simplicity's sake, but as Jas points out below I could set static fields like that on registration using REST API claims exchanges. So to make my question more complete, let me add that I also want to be able to update claims after initial registration. For example, a user might be assigned one role on initial registration, and then be moved to a different role later. I would want to update the user's claims so that I could perform authorization on the user based on the updated claims in her access token. But if I cannot guarantee that those claims will always be up to date then I will need a fallback to my own database / service which would effectively make AD B2C an authentication provider with no authorization utility.

Adrian Frielinghaus
  • 658
  • 1
  • 9
  • 15

1 Answers1

2

You should move the logic into the B2C user flow, and this would all be resolved rather than performing it after authentication. B2C doesn’t refresh token claims on refresh token flows (yet).

https://learn.microsoft.com/en-gb/azure/active-directory-b2c/rest-api-claims-exchange-dotnet

Jas Suri - MSFT
  • 10,605
  • 2
  • 10
  • 20
  • Hi Jas, thanks for your answer. That would work for static claims, like UserId, not for dynamic claims that need to be updated after registration. It would be great to be able to build authorization policies based on claims I set on B2C, but I guess that won't work with B2C at the moment. Do you have any idea if "refresh token claims on refresh token flows" is on the roadmap for B2C at all? It seems very limited in application without the ability to update claims after registration. – Adrian Frielinghaus Feb 02 '20 at 14:23
  • It is on the roadmap, i cant give an idea on a timeline. If the app is about to set something after registration, why does it need it in the token of the same session? And why cant the B2C flow set this dynamically during the initial sign up? – Jas Suri - MSFT Feb 03 '20 at 14:13
  • 1
    Well it depends what you mean by "session". As far as I can tell, the custom claims don't get added to the token until the user manually logs in again. So this "session" could continue for weeks or months if the token keeps getting refreshed. If the refreshing of the token also added new claims, then all would be well, but it doesn't seem to. So I have to force the user to log out and log in again every time I want a change in his claims to propogate to the token. – Adrian Frielinghaus Feb 03 '20 at 18:25
  • If the user hasn't logged out, then technically its the same web session, so the Application can maintain that claim for the first login/sign up. On subsequent logins, it uses the token claim. This means you wont have to force a logout. – Jas Suri - MSFT Feb 04 '20 at 18:50
  • Adrian did you sort this out? We have (i think) a smiliar situation. When a new user is created via ad b2c we then redirect them to a place in our app where they can create a "company account". We need to then log them out so they log in again and the company account id gets added (via custom claims via REST API) to the token. Not ideal flow. – tank104 Nov 25 '20 at 19:11
  • tank104: Use the id_token_hint flow. That will force them through a new journey with little or no interaction and get the new claim set. You app will generate this id_token_hint and send the user to B2C with it after you complete your app side logic. Sample usage [here](https://github.com/azure-ad-b2c/samples/tree/master/policies/invite) – Jas Suri - MSFT Nov 27 '20 at 09:39
  • @JasSuri-MSFT I find your proposed solution confusing. It seems you're talking about persisting state in the frontend application, but it's the backend that needs to check the claims in the JWT. Checking the db on every request is exactly what the JWT is designed to avoid. Has there been any progress on refreshing the token claims client-side? Is my best bet to direct the user to a custom policy flow with no interaction so b2c generates a new token? – darnmason Mar 23 '21 at 03:09
  • My use case is that I'm attempting to treat the token as an opaque string as the specs state you should, so I pass my access token to the server, and the server returns user data from a specific endpoint. Then we display the first and last name in the header. This issue is that when you trigger a edit profile user flow, the browser now keeps two access tokens but the sign in policy access token has stale claim data, and by default uses that It would make more sense that the edit profile policy triggers a clear internal msal cache of access tokens since they would all contains stale data – Dmitri Larionov Apr 29 '21 at 18:41
  • Wouldn't it make sense to enable the developer a public api function in msal to manually trigger clearing of cached access tokens, or do it internally in msal as soon as an edit profile policy is used. Not sure but as of now I'm forced to log the user out after they update their profile data since otherwise, I can no longer use the access token associated with the sign in policy. Can anyone provide a link to a proper usage of the edit profile policy, as all the examples I've found don't consider msal persisting stale token claims. – Dmitri Larionov Apr 29 '21 at 18:43
  • @darnmason yes, I will be documenting it in the coming weeks. And then the silent token calls will update the claim set. I think this will solve your problem too around edit profile – Jas Suri - MSFT Apr 30 '21 at 07:10
  • I went with creating a `B2C_1A_Refresh` custom policy that takes an `id_token_hint` with the user's `objectId` to sign them in automatically. When an action is performed that affects the claims we use MSAL login function with that policy to update the token. – darnmason May 01 '21 at 22:41
  • 2
    Ouch, this is kind of how the new refresh token journey works! Hopefully get the doc out soon. – Jas Suri - MSFT May 03 '21 at 08:38
  • @JasSuri-MSFT This is exactly what we are experiencing - claims returned in our current TokenRefresh journey are not incorporated into the access_token. How can I get more information on the new refresh token journey? Is there an example we can base our work on? – Jason Jun 29 '21 at 21:41
  • Looking to release in a couple more months. – Jas Suri - MSFT Jun 29 '21 at 22:00
  • @JasSuri-MSFT Any info on the release? And any doc / link that we can follow for the update / progress? – daniel May 14 '22 at 23:47
  • Unfortunately not at this moment in time. – Jas Suri - MSFT May 15 '22 at 07:43
  • @JasSuri-MSFT any update on this ? – tchelidze Jul 26 '22 at 08:36
  • We fixed the issues, maybe release in a few weeks. – Jas Suri - MSFT Jul 26 '22 at 17:37
  • @JasSuri-MSFT Hi Jas, are there any updates on when it will be globally available? We would like to use and utilize this fix. – Andrii Iasko Nov 04 '22 at 11:57
  • @JasSuri-MSFT any heads up on what the fix is and how we will use it? Very keen to have this too – The Senator Nov 25 '22 at 14:07
  • @JasSuri-MSFT Do you have any update on the subject, please? – Thomas Papapaschos Dec 20 '22 at 14:10
  • @JasSuri-MSFT Hi Jason, could you provide any update on this fix? I cannot move forward on implementing B2C without this feature. – Juan P. Garces Feb 15 '23 at 18:19
  • The latest starter pack includes the refresh token journey: https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack. The hook is via the `endpoint`: https://learn.microsoft.com/en-gb/azure/active-directory-b2c/relyingparty#endpoints – Jas Suri - MSFT Feb 16 '23 at 08:58
  • May i know this issue is resolve yet? – nullmicgo Jul 17 '23 at 22:54