How to retrieve user's consented version from ad b2c? - azure

I am using AZure Ad B2C tenant and i have custom Consent field on signup. This is working fine and whenever we update consent its prompting to user.
I have to keep history of user's consent. Lets say on signup user1 accepted Consent (v1) and some time later user1 accepted Consent (v2), etc.
I need the history of all consents accepted/declined by user. Is that possible to retrieve?
I have tried the following.
Added two extension claim along with consent field with string type (since stringcollection is not supported for extension claims)
<ClaimType Id="extension_TermsOfUseConsented">
<DisplayName>Terms of Use Consented</DisplayName>
<DataType>string</DataType>
<UserInputType>CheckboxMultiSelect</UserInputType>
<Restriction>
<Enumeration Text="I am agreeing to the terms of use" Value="2018-10-29" SelectByDefault="false" />
</Restriction>
</ClaimType>
<ClaimType Id="extension_TempConsent">
<DisplayName>temp Consent</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="extension_ConsentHistory">
<DisplayName>consent history</DisplayName>
<DataType>string</DataType>
</ClaimType>
Added the above field in signup relyingparty section.
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn-withConsent" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<InputClaims>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="extension_TermsOfUseConsented" />
<OutputClaim ClaimTypeReferenceId="extension_ConsentHistory" />
<OutputClaim ClaimTypeReferenceId="extension_TempConsent" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
In user journey step 3, reading the above claim in "AAD-UserReadUsingObjectId" 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" />
<OutputClaim ClaimTypeReferenceId="extension_TermsOfUseConsented" />
<OutputClaim ClaimTypeReferenceId="extension_TempConsent" />
<OutputClaim ClaimTypeReferenceId="extension_ConsentHistory" />
<OutputClaim ClaimTypeReferenceId="extension_ReadStringFromVP" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
In step 4, reading consent value and if terms is new then prompting user to accept. In claims exchange calling below technical profile.
<TechnicalProfile Id="SelfAsserted-Consent">
<DisplayName>User Consent</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.selfasserted.consent</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_TermsOfUseConsented" Required="true" />
<OutputClaim ClaimTypeReferenceId="extension_TempConsent" DefaultValue="," />
<OutputClaim ClaimTypeReferenceId="extension_ConsentHistory" DefaultValue="," />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="AppendConsent" />
<OutputClaimsTransformation ReferenceId="TakeConsentBackup" />
</OutputClaimsTransformations>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-WriteUserConsentByObjectId-ThrowIfNotExists" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
In ClaimsTransformation doing some string manupulation
<ClaimsTransformation Id="TakeConsentBackup" TransformationMethod="FormatStringMultipleClaims">
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_ConsentHistory" TransformationClaimType="inputClaim1" />
<InputClaim ClaimTypeReferenceId="extension_TempConsent" TransformationClaimType="inputClaim2" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{1},{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_ConsentHistory" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="AppendConsent" TransformationMethod="FormatStringMultipleClaims">
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_TermsOfUseConsented" TransformationClaimType="inputClaim1" />
<InputClaim ClaimTypeReferenceId="extension_TempConsent" TransformationClaimType="inputClaim2" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{1}{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_TempConsent" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
In AAD-WriteUserConsentByObjectId-ThrowIfNotExists writing the extension claim as below.
<TechnicalProfile Id="AAD-WriteUserConsentByObjectId-ThrowIfNotExists">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId" />
<PersistedClaim ClaimTypeReferenceId="extension_TermsOfUseConsented" />
<PersistedClaim ClaimTypeReferenceId="extension_ConsentHistory" />
On claim return, its showing string manipulated values (on time on consent)
but on next login (without consent) its not returning the extension field. getting only extension_TermsOfUseConsented field value.
enter image description here
The expected claim extension manipulated in policy only not from user input. Is that a problem?
what i am missing here?
thanks in advance.

Related

Azure B2C custom Sign up not displaying input fields

So I am trying to build a custom sign up page, and I need the first name, last name and phone number.
I am building out a proof of concept first so I am using one of the templates to see if i can push it to our API.
In the Extensions I have this technical profile.
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmailCustom">
<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="ContentDefinitionReferenceId">api.localaccountsignup</Item>
<Item Key="language.button_continue">Create</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="GetCurrentDateTime" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_termsOfUseConsentChoice" DefaultValue="AgreeToTermsOfUseConsentNo" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="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="givenName" PartnerClaimType="FirstName" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="LastName" />
<OutputClaim ClaimTypeReferenceId="extension_termsOfUseConsentChoice" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
<!-- During sign up, write the current time of consent, and version -->
<TechnicalProfile Id="AAD-UserWriteUsingLogonEmail">
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="currentTime" PartnerClaimType="extension_termsOfUseConsentDateTime" />
<PersistedClaim ClaimTypeReferenceId="extension_termsOfUseConsentChoice" />
<PersistedClaim ClaimTypeReferenceId="extension_termsOfUseConsentVersion" DefaultValue="V1" />
</PersistedClaims>
</TechnicalProfile>
This profile seems to work fine.
I see all the fields thats are in the output claims which is givenName and Surname and obviously the email and password.
But I want to add phone number and zip code.
When i add the following claims.
<OutputClaim ClaimTypeReferenceId="extension_phonenumber" PartnerClaimType="phonenumber" />
<OutputClaim ClaimTypeReferenceId="postalCode" PartnerClaimType="zipcode" />
I get this issue when i try to render the sign up page.
The page cannot be displayed because an internal server error has occurred.
I have made sure in my base file this is in the input claim
<InputClaim ClaimTypeReferenceId="postalCode" />
Im kind of at a loss for how to collect this user information..
They shouldn’t have partnerclaimtypes. And make sure the claim definition of each output claim has a userInputType defined.

How do I make the email address (signInName) read only for the sign-in form

I am creating an Azure AD B2C custom policy for inviting users to user my applications. As part of the invite I set custom user attributes.
In my user journey, if the user exists I send them to a sign in screen with the email pre-populated.
How do I make the email address (signInName) field read only so that the user can't apply the invite to a different account?
This is the technical profile I have for signing in. The signInName is populated by my invite token, but it is not read only.
<TechnicalProfile Id="LocalAccountSigninWithReadOnlyEmail">
<DisplayName>Local Account Signin</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="setting.showSignupLink">false</Item>
<Item Key="SignUpTarget">SignUpWithLogonEmailExchange</Item>
<Item Key="setting.operatingMode">Email</Item>
<Item Key="ContentDefinitionReferenceId">api.selfasserted</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" Required="true" PartnerClaimType="signInName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="password" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CopySignInNameToReadOnly" />
</OutputClaimsTransformations>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
Create a readonly field.
<ClaimType Id="readOnlyEmail">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<UserHelpText/>
<UserInputType>Readonly</UserInputType>
</ClaimType>
Copy email via a claims transformation e.g.
<ClaimsTransformation Id="CreateReadonlyEmailClaim" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="mail" TransformationClaimType="inputClaim"/>
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}"/>
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readonlyEmail" TransformationClaimType="outputClaim"/>
</OutputClaims>
</ClaimsTransformation>
Invoke the transformation e.g. <InputClaimsTransformations>
and then use the readonly field in the screen.
I had to face the same problem, I mean read-only email for sign-in custom flow. First of all, you have to create read-only custom claim:
<ClaimType Id="ReadOnlyEmail">
<DisplayName>Verified Email Address</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
Next, you have to create two custom claims transformations:
<ClaimsTransformation Id="CopyEmailToReadOnlyEmail" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ReadOnlyEmail" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="CopyEmailToSignInName" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
And finally it is custom claims provider for sign-in with read-only email:
<ClaimsProvider>
<DisplayName>Local Account Sign In</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Password">
<DisplayName>Local Account Signin</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.selfasserted</Item>
<Item Key="setting.showCancelButton">false</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CopyEmailToReadOnlyEmail" />
<InputClaimsTransformation ReferenceId="CopyEmailToSignInName" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" />
<InputClaim ClaimTypeReferenceId="ReadOnlyEmail" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ReadOnlyEmail" Required="true" />
<OutputClaim ClaimTypeReferenceId="password" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
</OutputClaims>
<ValidationTechnicalProfiles>
<!-- check users password if user existed -->
<ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>

Sign in email domain validation in Azure AD B2C

I am using microsoft authentication using the steps shown in the this link.
This opens a Microsoft SSO page to sign in which works as expected. This allows us to sign in through different microsoft hosted emails such as your-email#hotmail.com, your-email#outlook.com etc. What I want to do now is to add a validation to make the email domain bound for example only allow #outlook.com users to sign in. How can we do this using custom policy?
We tried adding following code suggested in this link:
<ClaimsTransformation Id="GetDomainFromEmail" TransformationMethod="ParseDomain">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="emailAddress" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="domain" TransformationClaimType="domain" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="LookupDomain" TransformationMethod="LookupValue">
<InputClaims>
<InputClaim ClaimTypeReferenceId="domain" TransformationClaimType="inputParameterId" />
</InputClaims>
<InputParameters>
<InputParameter Id="wintellect.com" DataType="string" Value="valid" />
<InputParameter Id="microsoft.com" DataType="string" Value="valid" />
<InputParameter Id="test.com" DataType="string" Value="valid" />
<InputParameter Id="errorOnFailedLookup" DataType="boolean" Value="true" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="domainStatus" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
But could not upload this policy and got this error:
Validation failed: 1 validation error(s) found in policy "B2C_1A_SIGNUP_SIGNIN" of tenant "<yor-tenant-id>.onmicrosoft.com".Invalid technical profile with id "Common-AAD" only the protocol handler ""Web.TPEngine.Providers.SelfAssertedAttributeProvider"" can have a ValidationTechnicalProfile.Invalid technical profile with id "Common-AAD" only the protocol handler ""Web.TPEngine.Providers.SelfAssertedAttributeProvider"" can have a ValidationTechnicalProfile.
Any help on how to verify domain in Microsoft SSO would be highly appreciated.
Thank you.
EDIT:
The technical profile is as follows:
<TechnicalProfile Id="Common-AAD">
<DisplayName>Microsoft Account or Microsoft-Connected Account</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="authorization_endpoint">https://login.microsoftonline.com/common/oauth2/v2.0/authorize</Item>
<Item Key="client_id">my-client-id</Item>
<Item Key="DiscoverMetadataByTokenIssuer">true</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="IdTokenAudience">my-id</Item>
<Item Key="response_types">id_token</Item>
<Item Key="scope">openid profile email offline_access</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/,https://sts.windows.net/</Item>
<Item Key="METADATA">https://login.microsoftonline.com/common/.well-known/openid-configuration</Item>
</Metadata>
<CryptographicKeys>
<!-- Make sure to update the reference ID of the client secret below you just created (B2C_1A_AADAppSecret) -->
<Key Id="client_secret" StorageReferenceId="B2C_1A_AppSecret" />
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" />
<OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" />
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="sub" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="preferred_username" />
<OutputClaim ClaimTypeReferenceId="otherMails" />
<OutputClaim ClaimTypeReferenceId="signInName" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>

Azure Active Directory B2C Custom Policy - Incoming name claims from jwt invite policy

I am trying to pass surname, givenName, and displayName claims from a JWT token using the id_token_hint parameter as specified in this sample: https://github.com/azure-ad-b2c/samples/tree/master/policies/invite
I have followed similar steps to those specified in this post:Azure Active Directory B2C Custom Invite Policy - Passing Custom Claims Between Steps
My issue is that the name claims are not persisted to the user's profile (I can see they exist in the id_token_hint parameter) and are also not provided in the access token, even though email and testclaim1 are returned in the token.
My onboarding RP:
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="givenName" />
<InputClaim ClaimTypeReferenceId="surname" />
<InputClaim ClaimTypeReferenceId="displayName" />
<InputClaim ClaimTypeReferenceId="testclaim1" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="testclaim1" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="Local" />
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
IdTokenHint_ExtractClaims technical profile:
<DisplayName>My ID Token Hint ClaimsProvider</DisplayName>
<!--Required for inviting user with token-->
<TechnicalProfiles>
<TechnicalProfile Id="IdTokenHint_ExtractClaims">
<DisplayName> My ID Token Hint TechnicalProfile</DisplayName>
<Protocol Name="None" />
<Metadata>
<Item Key="METADATA">{Settings:WebAppInviteUrl}</Item>
<!-- <Item Key="IdTokenAudience">your_optional_audience_override</Item> -->
<!-- <Item Key="issuer">your_optional_token_issuer_override</Item> -->
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="testclaim1" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
LocalAccountSignUpWithReadOnlyEmail technical profile:
<TechnicalProfile Id="LocalAccountSignUpWithReadOnlyEmail">
<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>
<!-- Sample: Remove sign-up email verification -->
<Item Key="EnforceEmailVerification">False</Item>
</Metadata>
<InputClaimsTransformations>
<!--Sample: Copy the email to ReadOnlyEamil claim type-->
<InputClaimsTransformation ReferenceId="CopyEmailAddress" />
</InputClaimsTransformations>
<InputClaims>
<!--Sample: Set input the ReadOnlyEmail claim type to prefilled the email address-->
<InputClaim ClaimTypeReferenceId="ReadOnlyEmail" />
<InputClaim ClaimTypeReferenceId="displayName" />
<InputClaim ClaimTypeReferenceId="givenName" />
<InputClaim ClaimTypeReferenceId="surname" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="givenName" />
<PersistedClaim ClaimTypeReferenceId="surname" />
<PersistedClaim ClaimTypeReferenceId="displayName" />
<PersistedClaim ClaimTypeReferenceId="testclaim1" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<!-- Sample: Display the ReadOnlyEmail claim type (instead of email claim type)-->
<OutputClaim ClaimTypeReferenceId="ReadOnlyEmail" 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" />
<!-- If the user has already verified their email address -->
<OutputClaim ClaimTypeReferenceId="extension_EmailIsVerified" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="displayName" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
<!-- Sample: Disable session management for sign-up page -->
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
AAD-UserWriteUsingLogonEmail technical profile:
<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="DisableStrongPassword, DisablePasswordExpiration" />
<PersistedClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="strongAuthenticationPhoneNumber" />
<!-- Optional claims. -->
<PersistedClaim ClaimTypeReferenceId="givenName" />
<PersistedClaim ClaimTypeReferenceId="surname" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="displayName" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
I didn't figure out the reason this was occurring, but I did discover a workaround. I updated the jwt token to change the names of givenName, surname, and displayName properties and then I added the PartnerClaimType attribute to the relying party InputClaim elements to map my jwt claims back to what my custom policy expects
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="firstName" />
<InputClaim ClaimTypeReferenceId="surname" PartnerClaimType="lastName" />
<InputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="adDisplayName"/>
</InputClaims>

Field Won't Show in Azure AD B2C Custom Policy

The following field won't show up on my signin page, why not?
In TrustFrameworkExtensions.xml
<ClaimType Id="extension_AssociateID">
<DisplayName>Associate ID - associateId</DisplayName>
<DataType>string</DataType>
<DefaultPartnerClaimTypes>
<Protocol Name="OAuth2" PartnerClaimType="associateId" />
<Protocol Name="OpenIdConnect" PartnerClaimType="associateId" />
</DefaultPartnerClaimTypes>
<UserInputType>Readonly</UserInputType>
</ClaimType>
I've added it to my TechnicalProfile
...
<InputClaim ClaimTypeReferenceId="extension_AssociateID" />
...
<OutputClaim ClaimTypeReferenceId="extension_AssociateID" Required="true"/>
To debug it, I've added a bunch of variations and they all work.
the label is {id after extension} - {PartnetClaimType}
I threw in the towel and decided to use the associate_id field name even though that was not what I wanted and then associate_id stopped working too. This lead me to the fix.
The fix was to remove it from my OutputClaims in my TechnicalProfile AAD-UserWriteUsingLogonName
<TechnicalProfile Id="AAD-UserWriteUsingLogonName">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.userName" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.userName" />
<PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="strongAuthenticationEmailAddress" />
<PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password" />
<PersistedClaim ClaimTypeReferenceId="displayName" DefaultValue="DoNotUse" />
<PersistedClaim ClaimTypeReferenceId="extension_associate_id" />
<PersistedClaim ClaimTypeReferenceId="extension_organization_id" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<!-- <OutputClaim ClaimTypeReferenceId="extension_associate_id" /> -->
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>

Resources