Why we need multiple token requests:
We're using implicit flow for our SPA to get access tokens from Azure AD B2C to access APIs that are protected by the B2C. We need to access multiple APIs and those are all registered as different applications in B2C, hence different audience for each. Since they are all different audience, B2C doesn't support a single token request for multiple audiences, so we'd have to make multiple token requests.
Background on B2C setup
We support local account login, as well as social login which is using our other Azure AD identity provider. We're also using custom policies for our B2C (identity experience framework)
The issue
The issue happens for user using the Azure AD login social login. The user has logged in before.
When multiple requests are made, we noticed the following network trace in google chrome:
The trace above showing:
Line 1 & 2 are the token request to B2C authorize endpoint for 2 different api/scope/audience.
Line 3+4 & line 5+6, those are redirects to login.windows.net and login.microsoftonline.com both as 1 set for a particular api/scope/audience.
Line 7 & 8 are both the the response (id token) form post back to B2C. The line 7 returns a bad request response from the form post.
The questions
Why the need to redirect back to login.windows.net or login.microsoftonline.com? Since the user has logged in before, shouldn't he has a valid session and thus B2C can just return the token requested?
Can B2C support concurrent token request (or login) from the same browser for social login identity? We're suspecting this is due to the auth state that B2C expect from social login is only one and unique, so concurrent login causes this to override each other which then cause the other request to be invalid. There is no details at all on the bad request response. It just shows a blank page with "Bad request" text.
-- Update March 5th, 2019 --
After some tinkering on B2C custom policies, I've managed to suppress the redirects, after having logged in once, by changing the following:
<TechnicalProfile Id="SM-SocialLogin">
<DisplayName>Session Mananagement Provider</DisplayName>
<!--Changed to this provider instead of ExternalLoginSSOSessionProvider-->
<Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.DefaultSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="alternativeSecurityId" />
<PersistedClaim ClaimTypeReferenceId="objectId" />
... removed for brevity ...
<PersistedClaim ClaimTypeReferenceId="groups" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectIdFromSession" DefaultValue="true"/>
</OutputClaims>
</TechnicalProfile>
Changes made is to use default session provider.
Why the external session provider would not suppress re-authenticating though? The metadata AlwaysFetchClaimsFromProvider set to false would not suppress re-authenticating as well.
But going with this workaround cause us another problem which is asked in a separate question.
Related
I have been slowly working through a custom policy to handle a JIT migration of an external IDP users to Azure B2C. It has been a long journey because there are many complexities in sorting out the components and the configuration steps using XML is a bit of a challenge. I am making some progress.
The basic use case is this: At sign in, the user/password is first checked against the B2C AD and if the user already exists just continue to sign in. If the user isn't in B2C AD, then check the external IDP with a REST call. I planned the response from the external IDP to be used in a binary way: If I get a 200 response, then add the user to B2C AD. If an error is returned then the user will be told to create an account.
I started with a couple of samples from a JIT Migration that I found. I had some difficulty navigating all the components with the social media logins, etc that I then reverted back to the "Local Accounts" sample found in the custom policy starterpack. I got the basic steps to work with no modification. I then added the technical profiles for the REST calls to the extensions file. I used the extension from one of the JIT migration samples and at least got the uploads to work. I am able to present a login prompt.
These are the scenarios and responses I see: 1) A user that I know is in B2C returns an error of "Unable to validate the information provided." 2) A user that I know is in the external IDP but not in B2C returns "We can't seem to find your account". 3) If I try a completely made up account that exists in neither, I see "The claims exchange 'REST-UserMigration-LocalAccount-SignIn' specified in step '1' returned HTTP error response with Code 'BadRequest' and Reason ''. "
So the question/struggle is how do I best sort out what is failing in each step. I have added app insights to the environment. I do see the traces in VS Code. But what should I look for? Here is the short responses ("Exceptions") for each of the errors listed above:
Scenario 1: The claim type "signInNames.emailAddress", designated as the identifier claim type, could not be found in the claims collection for the claims principal in tenant id "B2C_1A_JITMigration_signup_signin"
Scenario 2: A user with the specified credential could not be found.
Scenario 3: ErrorCodes: AADB2C90075
Rather than post of the code, here is the link to what I copied: https://github.com/azure-ad-b2c/user-migration/blob/master/jit-migration-v2/policy/TrustFrameworkExtensions.xml I can post up my exact code but basically I changed the tenant and the REST call. The Technical profile for the REST call snippet is:
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="username"/>
<InputClaim ClaimTypeReferenceId="password" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="needToMigrate" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="newPassword" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
The input claims are fine but I am not sure how to modify the output claims. The REST API returns a single value of an authentication token which I don't need but is an indication that the authentication succeeded. It returns an error if the authentication fails. Basically an HTTP 200 is good, an HTTP 400 means the user doesn't exist. The goal is to use that API call as a binary indicator to either migrate or not migrate the user. Perhaps I cannot use the API in that way.
My hope is to solve each scenario step by step. Figure out why the scenario 1 doesn't work although the credential are correct. Then figure out why scenario 2 is correctly identifying that the user isn't found but wasn't automatically migrated. Finally, to figure out how to manage the 400 error returned by the API when the user doesn't exist in the external IDP.
I might need to tweak the user journeys or orchestration steps? Taking baby steps here.
Thanks
I am using the techniques described in AD B2C->Automation->Account Management to get an Access token for an application. This is working fine, but I want to add some custom claims to the returned token. Is there some way I can use Custom Policies to add custom claims when an application requests a token?
Note: I already have custom policies that I use for interactive signons, which add claims to the returned token. I would like to somehow use these in a non-interactive mode.
I already have custom policies... I would like to somehow use these in a non-interactive mode.
For non-interactive authentication/authorization w/ B2C that would the ROPC (Resource Owner Password Credentials) flow.
In the relying party file you can output custom claims:
<OutputClaim ClaimTypeReferenceId="sub" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="myCustomClaim" DefaultValue="" />
I'm using Azure AD B2C.
I've created a Sign up v2 user flow with multifactor authentication enabled. When I run the user flow and go through the sign up process including MFA via SMS to my specified mobile phone number, I'm returned to the reply URL that I've configured - jwt.ms.
The id token has return claims including my email address as well as other attributes that I've configured to return, but nothing regarding the mobile phone number used for MFA. There doesn't appear to be a way to configure the user flow to include this in the return claims. Does anyone know if this is possible and if so, how to do it?
Cheers.
The phone number is read from and written to the strongAuthenticationPhoneNumber property of the user object.
Currently, this property is not available to a built-in policy (i.e. a user flow), but it is available to a custom policy.
If you use the custom policy starter pack for MFA, then you can add the strongAuthenticationPhoneNumber claim, as an outgoing claim in the ID token, as follows:
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="strongAuthenticationPhoneNumber" PartnerClaimType="phone_number" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
This article https://github.com/Azure-Samples/active-directory-b2c-advanced-policies/blob/master/Documentation/Domain-and-login-hint%20Tips%20and%20Tricks.pdf explains how to pass the login_hint to an ADFS login page. My TechnicalProfile for the ADFS ClaimsProvider has the InputClaims defined as shown below. (The username contains the user login name from a previous user journey step.)
<InputClaims>
<InputClaim ClaimTypeReferenceId="username" PartnerClaimType="login_hint" />
</InputClaims>
Doing the exact same for an AAD Claims provider works just fine, but it does not work for the SAML2 protocol. According to the article it is supposed to work. Am I doing something wrong here?
I have set up an Azure B2C tenant and used custom policies to add azure ad as an IDP so that users can sign up with their domain accounts. I can build a custom page where ask them for their email and then redirect them to the proper policy(one for work domain accounts and another for personal emails), so that they do not have to make the choice between work and personal emails. The problem is that I do not want to make the user enter the email once again. Is there a way/option to do this? I basically want to achieve something similar to what the common endpoint of Azure AD does for all accounts.
For a custom policy, if you add the "login_hint" query string parameter to the OpenID Connect authentication request, then you can default the login field to this login hint by adding the "DefaultValue" attribute to the "signInName" input claim for the "SelfAsserted-LocalAccountSignin-Email" technical profile as follows:
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<DisplayName>Local Account Signin</DisplayName>
...
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" DefaultValue="{OIDC:LoginHint}" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
...
</OutputClaims>
...
</TechnicalProfile>
The "DefaultValue" attribute references a claims resolver that sets the "signInName" claim type to the "login_hint" parameter of the OpenID Connect authentication request.
See the Set up direct sign-in using Azure Active Directory B2C article for more information about passing the "login_hint" query string parameter.