For some reason my token configuration is not respected when using authorization code flow (response_type=code). organization, and displayName are missing from the both the id and auth tokens.
When I use implicit grant flow (response_type=id_token) it works as expected (organization, and displayName are present in the id token).
The configuration is included below. What is the reason?
<UserJourneys>
<UserJourney Id="DefaultSignin">
<OrchestrationSteps>
<OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
</UserJourney>
</UserJourneys>
<RelyingParty>
<DefaultUserJourney ReferenceId="DefaultSignin" />
<TechnicalProfile Id="JWTSignin">
<DisplayName>JWT Sign In</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="sub" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="organization" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
The reason for this was that I had Protocol=None and I should've had Protocol=OpenIdConnect
Two strange things:
why does it work for implicit flow at all then?
I'm sure I didn't invent this, it must come from one of the "custom policy starter packs" or other MSFT examples
<TechnicalProfiles>
<TechnicalProfile Id="JwtIssuer">
<DisplayName>JSON Web Token Issuer</DisplayName>
- <Protocol Name="None" />
+ <Protocol Name="OpenIdConnect" />
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="client_id">{service:te}</Item>
• When you are using authorization code flow, i.e., response_type=code, the organization and display name are missing in the client_id and the authorization token because in authorization code flow, the access token is passed over a secure connection with HTTPS SSL and TLS encryption as it is issued by the OAuth provider which us a mandate since, it is not possible to pass an access token directly in a URL parameter because URL parameters are part of the HTTP Request, which in turn could possibly be a prey of the Man-in-the-middle attacks. Thus, when you use authorization code flow to pass the token, an intermediary one-time-use "authorization code" is provided that only the legitimate receiver will be able to exchange (because you need the client secret) and that the code will be useless to potential hackers intercepting the requests over unencrypted transactions (because they don't know the client secret).
• Similarly, in implicit flow, the communication regarding the authorization token is less secure where there may be potential attack vectors like spoofing the domain upon redirect by hijacking the IP address of the client’s website since the access token in implicit flow is passed directly as a hash fragment which is not a part of the URL parameter though they can be read by running java scripts on the client side in the browser due to which you are able to read the issuer’s data by accessing the token hash through implicit flow while in authorization code flow, the data to be read is intended to read by the client possessing the private key of the HTTPS SSL connection certificate because even though you access the token, it is secured by the authorization code which should be decrypted by the client ID/client secret and then decrypted over a HTTPS SSL connection only.
• Thus, the details are not shown in authorization code flow as the token is intercepted in between wherein it is meant for authorizing the access connection at the client side over HTTPS.
Please find the below links for more information: -
Why is there an "Authorization Code" flow in OAuth2 when "Implicit" flow works so well?
https://aaronparecki.com/oauth-2-simplified/
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="" />
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.
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>
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.