Reputation: 160
UPDATE 1 2021-10-24
INITIAL POST
We recently upgraded our SPAs from MSAL 0.x to 2.x, upgrading to use OAuth 2.0 Authorization Code Flow with PKCE in B2C. This is complete and working with 2.16.1, and we have tried upgrading to 2.18.0 without success to solve the below 'issue'.
As part of the upgrade work, we noticed that if an existing authentication result exists for the tenant, the login redirect is bypassed and we immediately handle the authentication process (AKA acquireTokenSilent()
), which is great since if we just logged in and head to a different tab for the SPA we are logged in on that tab immediately.
However, we use different SPAs with different B2C policies in the same tenant, and we are unexpectedly getting the authentication result from one SPA policy for a different SPA policy (in the same tenant). Our application catches this and reports an error (since the claims in the access tokens are incompatible), but we are trying to understand what we can do to prevent this confusing situation.
For background, I will mea culpa here and say we were using MSAL 0.1.5 (!!). Under this version, each SPA authenticated separately, and each SPA could be logged into the same tenant simultaneously without interference (being attached to different domains).
With the issue in MSAL 2.x this is no longer the case, and it seems that authentication (specifically, claims) are 'leaking' between B2C policies. I have checked the msal.PublicClientApplication()
for additional settings but see nothing. I do see a forceRefresh
flag for acquireTokenSilent()
but that indicates it is for network token check and switching it on has made no difference to getting cross policy authentication results.
What we do notice is that the authority included in the response (being passed thru via the handleRedirectPromise()
) is the CORRECT policy id for the 2nd SPA. In addition, when we use acquireTokenSilent()
the access token presented to our web server has the CORRECT policy id (in the tfp
claim) of 2nd SPA, but the claims from that access token are for the 1st SPA's policy and authenticated user. Note we specifically validate the tvps.AuthenticationType
for the 2nd SPA which is correct since the tfp
was updated!
Here is the reproduction sequence (in abridged form):
acquireTokenSilent()
for SPA 1acquireTokenSilent()
for SPA 2tfp
claim matches the 2nd SPA's policy id(*) It appears that when the msal.PublicClientApplication()
/loginRedirect()
is performed, if there is a recent login the B2C login page redirect is bypassed and we immediately go into the handleRedirectPromise()
. It seems this is where the authentication result from the old SPA 1 login gets passed back. Note that the response's authority IS correct for SPA 2.
Here is the format of the authority field in the msal.PublicClientApplication()
config:
authority: `https://<ourtenant>.b2clogin.com/tfp/<ourtenant>.onmicrosoft.com/B2C_1_${policyId}`
where:
policyId has the form of 'sign-up-in-spa
' or 'sign-up-in-spa-phn
'
So:
Upvotes: 0
Views: 985
Reputation: 160
Update 2021-10-29
We ran into an issue with the Single sign-on-configuration set to Application. It seems that when logging out (and we are policy based) it left the SSO cookie in the tenant, and thus when going to sign-in again (after explicitly signing out) you were immediately signed in again (within the Web app session lifetime setting timeframe).
Changing this setting to Policy resolved the issue - signing out now signs out of the policy's sign-in.
INITIAL ANSWER
The answer is that this behaviour is defined in the B2C user flows for Sessions. It appears our very old use of MSAL 0.15 completely ignored this setting, probably (?) because we were using Implicit Flow. The more secure Authorization Code with PKCE we guess enabled enforcement.
Here is the Microsoft article about B2C session behaviour:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/session-behavior?pivots=b2c-user-flow
In essence:
Thanks @JasSuri-MSFT as your comment tickled my brain about session management and SSO.
Upvotes: 1