UPDATE 1 2021-10-24
- We are using two different 'classic' sign-in/sign-up policies predating even MSAL 0.15. We don't use custom policies. I believe we can export them using the Manifest for the application, would that be useful? I would expect to redact identifiable information if I am posting here, but am not sure if that makes them useless for purpose...
- With respect to SSO, I think what @JasSuri-MSFT is saying is that because the claim set is the same/similar, the policy id itself is ignored (for SSO purposes)? Is this documented somewhere, such that we can get an idea of how different the claim sets need to be?
- Given that SSO behaviour, is there a way of getting the policy id itself to be used to discern SSO? This is why I made this initial post for MSAL 2.x because there might be something I am missing here in the configuration to restore previous behaviour.
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):
- Open up SPA 1 login page on tab 1 (*)
- Get redirected to B2C page for authentication
- Redirect back to SPA 1 where authentication is completed by
acquireTokenSilent()
for SPA 1 - Web server validates SPA 1 access token claims for SPA 1
- Open up SPA 2 login page on tab 2 (*)
- Redirect back to SPA 2 where authentication is completed by
acquireTokenSilent()
for SPA 2 - Web server rejects SPA 1 access token claims for SPA 2 since the claims values are incompatible with the API call, but the
tfp
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:
- Are we reading this situation correctly, that different B2C policies in the same tenant can reuse an existing authentication result?
- Is it possible to make MSAL (et al) enforce the B2C policy such it would only return existing the authentication result for the matching policy? That is, don't cross contaminate claims from different policies.
CodePudding user response:
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:
Thanks @JasSuri-MSFT as your comment tickled my brain about session management and SSO.