Unable to pass query param to azure-ad-b2c custom policy and store values - azure

I have a scenario where i have to pass a query parameter in the URL to my custom sign-up policy and so far all my attempts did not work. there seem to be something that i am missing following the guidelines i found in github. I am trying to pass LoyaltyNumber and i have this attribute defined in my policy as extension_LoyaltyNumber. Below is the snippet
my custom signup policy
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUp" />
<UserJourneyBehaviors>
<ContentDefinitionParameters>
<Parameter Name="LoyaltyNumber">{OAUTH-KV:LoyaltyNumber}</Parameter>
</ContentDefinitionParameters>
</UserJourneyBehaviors>
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="extension_LoyaltyNumber" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
<OutputClaim ClaimTypeReferenceId="extension_ValidPassword" />
<OutputClaim ClaimTypeReferenceId="extension_LoyaltyNumber" AlwaysUseDefaultValue="true"/>
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
in my TrustFrameworkExtension.xml, i have defined it in the Local Account as follows
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<!--Local account sign-up page-->
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
<Metadata>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_LoyaltyNumber" AlwaysUseDefaultValue="true" DefaultValue="{OAUTH-K:LoyaltyNumber}" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" />
</OutputClaims>
</TechnicalProfile>
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<Metadata>
<Item Key="setting.showSignupLink">false</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
i also have it in the building bolocks section as...
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="extension_LoyaltyNumber">
<DisplayName>Loyality-Number</DisplayName>
<DataType>string</DataType>
<UserHelpText>Your loyality from your membership card</UserHelpText>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>
i have it also to write to Azure Active Directory claims provider section as follows
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<!--Insert b2c-extensions-app application ID here, for example: 11111111-1111-1111-1111-111111111111-->
<Item Key="ClientId">11111111-1111-1111-1111-111111111111</Item>
<!--Insert b2c-extensions-app application ObjectId here, for example: 22222222-2222-2222-2222-222222222222-->
<Item Key="ApplicationObjectId">22222222-2222-2222-2222-222222222222</Item>
</Metadata>
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserWriteUsingLogonEmail">
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="extension_LoyaltyNumber" />
</PersistedClaims>
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserReadUsingObjectId">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_LoyaltyNumber" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
And the redirect happens from my web application using the following method
public void SignUpNewUser()
{
if (!Request.IsAuthenticated)
{
var authenticationProperties = new AuthenticationProperties();
authenticationProperties.Dictionary.Add("LoyaltyNumber", "556677");
authenticationProperties.RedirectUri = "/";
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties, Startup.SignUpPolicyId);
}
}
The result i am getting after the user sign-up for the custom attribute extension_LoyaltyNumber is {OAUTH-K:LoyaltyNumber}
Somehow the value 556677 i pass as a query param is not getting to this custom attribute and get stored in Azure user attribute
Can you help?
Thanks

Add
<OutputClaim ClaimTypeReferenceId="extension_LoyaltyNumber" />
to LocalAccountSignUpWithLogonEmail. Output claims allow the claim to be used in subsequent orchestration steps.
In the <RelyingParty> section, change your output claim to the following, its already resolved so no need to resolve it again here:
<OutputClaim ClaimTypeReferenceId="extension_LoyaltyNumber" />

What I do when I want to have query string parameters handled is this:
Create a TechnicalProfile:
<TechnicalProfile Id="GetMyParameter">
<DisplayName>GetMyParameter</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
<Metadata>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="myParameter" AlwaysUseDefaultValue="true" DefaultValue="{OAUTH-KV:myparameter}" />
</OutputClaims>
</TechnicalProfile>
Then run that profile in an OrchestrationStep before the claim value is required (it can be the first one or somewhere later along the way, doesn't matter much as far as I can tell).
You can have as many parameters in the technical profile as you wish. Works every time, doesn't crash if you don't provide value.

I Faced with the same problem, and found answer in official documentation
Don't forget to create claimType in your claims schema
<ClaimType Id="q_param">
<DisplayName>q_param</DisplayName>
<DataType>string</DataType>
<UserHelpText>Special parameter passed for authentication context</UserHelpText>
</ClaimType>
Additionally you need to set output claim in technical profile like in example
from TrustFrameworkExtensions.xml
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">ProxyIdentityExperienceFrameworkAppId</Item>
<Item Key="IdTokenAudience">IdentityExperienceFrameworkAppId</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="ProxyIdentityExperienceFrameworkAppId" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="IdentityExperienceFrameworkAppId" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="q_param" DefaultValue="{OAUTH-KV:q_param}" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>

Related

Azure B2C SAML Email Claim

I have successfully setup Azure B2C using the local account start packs using B2C as the IDP, we've integrated with a partner and can access their application. How do we go about Passing the local IDP accounts 'email' attribute to the partner as a claim. They're getting first, last name and display name without issue but not receiving the email claim.
Similar issue to this: Azure B2C SAML Custom Policy Assert Email
I've inserted this into the BaseFramework
<ClaimType Id="email">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="email" />
<Protocol Name="OpenIdConnect" PartnerClaimType="email" />
<Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email" />
</DefaultPartnerClaimTypes>
...
</ClaimType>
And this Technical Profile
<TechnicalProfile Id="AAD-UserReadUsingObjectId">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<OutputClaims>
<!-- Optional claims -->
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="otherMails" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
and then this in the SAML flow:
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignInWithCA" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="SAML2" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="objectId" />
</OutputClaims>
<SubjectNamingInfo ClaimType="objectId" ExcludeAsClaim="true" />
</TechnicalProfile>
</RelyingParty>
But it's still not working
As above modified as per documentation
You didn’t need to make any of these changes, except in the third snippet you needed to put this
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email" />
This takes the signInNames.emailAddress and issues it as “email” into the token.
Currently you defined a claim called email, read a claim called signInNames.emailAddress, then try to issue a claim called email into the token, which will be null.

Azure Ad B2C Error : "Unable to validate the information provided." when using custom policy and custom attribute

I am trying to use custom policies of Azure Ad B2C because we have a complex flow of self-registration for external users.
I am trying to use custom attributes, and I followed the dedicated tutorial.
I keep getting the error "Unable to validate the information provided" nevertheless.
1 - I added a section for AAD-Common with the ids of the b2c-extensionregistered app.
2 - I created custom attributes using the Azure AD B2C UI in the dedicated section.
3 - I declared this attribute in the ClaimSchemas section.
4 - I added the attribute in the self-asserted sign-up technical profile.
5 - I added the attribute to be persisted as a persistent claim in technical profile validating the self-sign-up.
6 - I uploaded the extension file on my Azure AD B2C custom policy page.
Can you tell me what I do wrong?
Thanks!
I double-checked several times the tutorial against my work before posting here.
Here is my extension file:
<?xml version="1.0" encoding="utf-8" ?>
<TrustFrameworkPolicy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06" PolicySchemaVersion="0.3.0.0" TenantId="wynsureformercer.onmicrosoft.com" PolicyId="B2C_1A_TrustFrameworkExtensions" PublicPolicyUri="http://mytenant.onmicrosoft.com/B2C_1A_TrustFrameworkExtensions">
<BasePolicy>
<TenantId>mytenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkLocalization</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="custom_employee_id">
<DisplayName>Employee Id</DisplayName>
<DataType>string</DataType>
<UserHelpText/>
<UserInputType>TextBox</UserInputType>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<!--Insert b2c-extensions-app application ID here, for example: 11111111-1111-1111-1111-111111111111-->
<Item Key="ClientId">f8070b57-64ba-blablabla</Item>
<!--Insert b2c-extensions-app application ObjectId here, for example: 22222222-2222-2222-2222-222222222222-->
<Item Key="ApplicationObjectId">a6ae751f-7d74-blablabla</Item>
</Metadata>
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserWriteUsingLogonEmail">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" Required="true" />
</InputClaims>
<PersistedClaims>
<!-- Required claims -->
<PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" />
<PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password" />
<PersistedClaim ClaimTypeReferenceId="displayName" DefaultValue="unknown" />
<PersistedClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration" />
<!-- Optional claims. -->
<PersistedClaim ClaimTypeReferenceId="givenName" />
<PersistedClaim ClaimTypeReferenceId="surname" />
<PersistedClaim ClaimTypeReferenceId="custom_employee_id" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">ProxyIdentityExperienceFrameworkAppId</Item>
<Item Key="IdTokenAudience">IdentityExperienceFrameworkAppId</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="3fb15547-7986-416b-b6ba-4d84bee6770a" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="1215af09-c958-4d92-a356-b1316895ff31" />
</InputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
<DisplayName>Email signup</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="newUser" />
<!-- Optional claims, to be collected from the user -->
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" />
<OutputClaim ClaimTypeReferenceId="custom_employee_id" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<!--UserJourneys>
</UserJourneys-->
</TrustFrameworkPolicy>
When using a custom attribute in custom policies, you must prefix the claim type ID with extension_ to allow the correct data mapping to take place within the Azure AD B2C directory.
Also, make sure to copy the Object ID of the b2c-extensions-app in your portal to ApplicationObjectId and Application (client) ID of b2c-extensions-app to the client id in your custom policy correctly.

Azure B2C Rest API Error Still Creating Account

I have created a REST API for Azure B2C to return a claim or an error during the account creation flow.
In my Custom Policy I have hooked up the API and it gets called.
However if the API returns either a 400 or 409, the account is still created but the user is presented with the error message on the create page. The user's account is still created despite the error.
The user then fixes the error and clicks create again but can't create the account because it was already created.
I have followed the instructions here:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-rest-api-claims-validation
My Claims Provider looks like this and claim from the REST API is called VerifiedDateOfBirth:
<ClaimsProvider>
<DisplayName>REST API</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-Validation">
<DisplayName>Check date of birth</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<!-- Set the ServiceUrl with your own REST API endpoint -->
<Item Key="ServiceUrl">{REST URL}}</Item>
<Item Key="SendClaimsIn">Body</Item>
<!-- Set AuthenticationType to Basic or ClientCertificate in production environments -->
<Item Key="AuthenticationType">None</Item>
<!-- REMOVE the following line in production environments -->
<Item Key="AllowInsecureAuthInProduction">true</Item>
</Metadata>
<InputClaims>
<!-- Claims sent to your REST API -->
<InputClaim ClaimTypeReferenceId="dateOfBirth" />
</InputClaims>
<OutputClaims>
<!-- Claims parsed from your REST API -->
<OutputClaim ClaimTypeReferenceId="VerifiedDateOfBirth" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
And the technical profile:
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
<DisplayName>Email signup</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item>
<Item Key="language.button_continue">Create</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="newUser" />
<!-- Optional claims, to be collected from the user -->
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" />
<OutputClaim ClaimTypeReferenceId="dateOfBirth" Required="true" />
<OutputClaim ClaimTypeReferenceId="VerifiedDateOfBirth" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
<ValidationTechnicalProfile ReferenceId="REST-Validation" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
When the error occurs I see the following error on the create page:
Do I need to add some additional configuration?
The order of your validation profiles matter in your LocalAccountSignUpWithLogonEmail technical profile. It looks like the first validation that was taking place was the writing of the user account.
Try this instead:
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="REST-Validation" />
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>

Only "email" field can be passed via "id_token_hint" to B2C custom policy

I'm following this article to pass email and displayName as id_token_hint to my custom policy. Following is the technical profile I'm using to extract the data:
<TechnicalProfiles>
<TechnicalProfile Id="IdTokenHint_ExtractClaims">
<DisplayName> My ID Token Hint TechnicalProfile</DisplayName>
<Protocol Name="None" />
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_ClientAssertionSigningKey" />
</CryptographicKeys>
<OutputClaims>
<!--Sample: Read the email cliam from the id_token_hint-->
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="newUserEmail"/>
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="displayName"/>
</OutputClaims>
</TechnicalProfile>
Problem is, I'm only able to extract the email value. The displayName claim type not getting extracted. I checked the id_token_hint value in jwt.ms, value for both email and displayName is present there. How can i solve this?
You must also add the incoming claims as the input claims for the RelyingParty technical profile.
Example
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<!-- The following claims are read from the ID token -->
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="newUserEmail" />
<InputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="displayName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />
...
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>

Sign-up policy - Set user attributes through code

I want to programatically set user attributes for the sign up policy. I saw a previous question (Pass parameters to Sign-up policy) asked over a year ago and it was not possible at the time. Any update on this?
Is this possible with the AuthenticationProperties.Dictionary property? Something like this?
HttpContext.GetOwinContext().Set("Policy", Startup.SignUpPolicyId);
var authenticationProperties = new AuthenticationProperties();
authenticationProperties.Dictionary.Add("myattribute", "myvalue");
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
This can be implemented using a custom policy.
A working sample of passing an input claim from a relying party application to a custom policy (e.g. an invitation flow as a sign-up policy) is here.
In the WingTipGamesWebApplication project, the InvitationController controller class has two action methods, Create and Redeem.
The Create action method sends a signed redemption link to the email address for the invited user. This redemption link contains this email address.
The Redeem action method handles the redemption link. It passes the email address, as the verified_email claim in a JWT that is signed with the client secret of the Wingtip Games application (see the CreateSelfIssuedToken method in the Startup class in the WingTipGamesWebApplication project), from the redemption link to the Invitation policy.
The Invitation policy can be found at here.
The Invitation policy declares the verified_email claim as an input claim:
<RelyingParty>
<DefaultUserJourney ReferenceId="Invitation" />
<TechnicalProfile Id="Invitation">
<InputTokenFormat>JWT</InputTokenFormat>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="WingTipGamesClientSecret" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_VerifiedEmail" />
</InputClaims>
</TechnicalProfile>
</RelyingParty>
The extension_verifiedEmail claim type, which is declared as a read-only field (so that it can't be modified by the end user), is mapped to the verified_email input claim:
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="extension_VerifiedEmail">
<DisplayName>Verified Email</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="verified_email" />
<Protocol Name="OpenIdConnect" PartnerClaimType="verified_email" />
<Protocol Name="SAML2" PartnerClaimType="http://schemas.wingtipb2c.net/identity/claims/verifiedemail" />
</DefaultPartnerClaimTypes>
<UserInputType>Readonly</UserInputType>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>
The Invitation user journey can be found in here.
The second orchestration step of the Invitation user journey executes the LocalAccount-Registration-VerifiedEmail technical profile:
<UserJourney Id="Invitation">
<OrchestrationSteps>
...
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
...
<ClaimsExchange Id="LocalAccountRegistrationExchange" TechnicalProfileReferenceId="LocalAccount-Registration-VerifiedEmail" />
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</UserJourney>
The LocalAccount-Registration-VerifiedEmail technical profile copies from the extension_verifiedEmail claim to the email claim and then displays the sign-up form with the verified email address (the extension_verifiedEmail claim):
<TechnicalProfile Id="LocalAccount-Registration-VerifiedEmail">
<DisplayName>WingTip Account</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.localaccount.registration</Item>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="language.button_continue">Create</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateEmailFromVerifiedEmail" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_VerifiedEmail" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_VerifiedEmail" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="displayName" Required="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="newUser" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="sub" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AzureActiveDirectoryStore-WriteUserByEmail-ThrowIfExists" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SSOSession-AzureActiveDirectory" />
</TechnicalProfile>
This LocalAccount-Registration-VerifiedEmail technical profile references the AzureActiveDirectoryStore-WriteUserByEmail-ThrowIfExists validation technical profile that saves the local account with the verified email address (the email claim):
<TechnicalProfile Id="AzureActiveDirectoryStore-WriteUserByEmail-ThrowIfExists">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="displayName" />
<PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" />
<PersistedClaim ClaimTypeReferenceId="givenName" />
<PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password" />
<PersistedClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration" />
<PersistedClaim ClaimTypeReferenceId="surname" />
<PersistedClaim ClaimTypeReferenceId="verified.strongAuthenticationPhoneNumber" PartnerClaimType="strongAuthenticationPhoneNumber" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateSubject" />
</OutputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="AzureActiveDirectoryStore-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SSOSession-AzureActiveDirectory" />
</TechnicalProfile>

Resources