Howto Enable Email Verification in Azure AD B2C - azure-ad-b2c

How do I enable email verification in B2C w/ custom policies?
In an attempt to reverse engineer it, I tried disabling it in a built-in policy and downloaded the policy. I tried adding that metadata item to my self-asserted technical profile but that didn't work.
Reverse Engineer Test | Built-In Policy
<TechnicalProfile Id="SelfAsserted-Input">
<Metadata>
<Item Key="EnforceEmailVerification">False</Item>
</Metadata>
</TechnicalProfile>
My Self-Asserted Technical Profile
<TechnicalProfile Id="LocalAccountSignUp">
<DisplayName>User signup</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="EnforceEmailVerification">True</Item>
</Metadata>
...
</TechnicalProfile>
I'm using usernames for local accounts incase that matters.

Whether a local account is created with an email address- or user name-based sign-in name, you add email verification by adding PartnerClaimType="Verified.Email" to the "email" output claim of your self-asserted technical profile, as follows:
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />

Related

Share session between two different (but very similar) TechnicalProfiles

Scenario is that during combined SignIn/SignUp flow I'm using following TechnicalProfile for signing in the user with local account:
Base.xml
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<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="SignUpTarget">SignUpWithLogonEmailExchange</Item>
<Item Key="setting.operatingMode">Email</Item>
<Item Key="ContentDefinitionReferenceId">api.signuporsignin</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="password" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
Extensions:
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<Metadata>
<Item Key="setting.forgotPasswordLinkOverride">ForgotPasswordExchange</Item>
</Metadata>
</TechnicalProfile>
Base.xml is taken from one of the starter-packs available on GitHub.
This is working fine and when I'm logged in and go to combined SignUp/SignIn policy again, I'm authenticated automatically without needing to provide any credentials or to select external IdP.
Now, for some other flows (ProfileEdit, PasswordChange) I've slightly modified SelfAsserted-LocalAccountSignin-Email technical profile to not show SignUp/ForgotPassword links.
Technical profile definition:
<TechnicalProfile Id="SelfAsserted-LocalAccountSigninOnly-Email">
<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="setting.forgotPasswordLinkLocation">none</Item>
</Metadata>
<IncludeTechnicalProfile ReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</TechnicalProfile>
As you can see, it includes the SelfAsserted-LocalAccountSignin-Email and just overrides two setting values.
However, now when the user is authenticated after executing combined SignUp/SignIn policy and he goes to my ProfileEdit policy, he is prompted to authenticate again.
This happens only if user was authenticated using local account, as in my case SignUp/SignIn & ProfileEdit/PasswordChange policies are using different (but very similar) SelfAsserted technical profiles. If user was authenticated using any social idp, ProfileEdit/PasswordChange policies work fine without prompting user for reauthentication.
Any ideas how to solve the issue for the local account?
One solution would be to use the same technical profile for combined SignUp/SignIn and ProfileEdit/PasswordChange and then hide links where necessary using JavaScript, however, if possible I would like to minimize changes made by JS.

B2C Sign-up screen shows {OIDC:LoginHint} instead of the login

I am passing an email of a prospective member in the login_hint from my website to B2C. In my custom policy I am setting the email claim of the "SignUp" TechnicalProfile to {OIDC:LoginHint}
<TechnicalProfile Id="CustomLocalAccountSignUpWithLogonEmail">
<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" DefaultValue="{OIDC:LoginHint}" />
</InputClaims>
<OutputClaims>
But instead of seeing the user's email, the string {OIDC:LoginHint} is displayed in the form:
There is a similar question from 2018 with a suggested workaround of using JavaScript to populate the email field on the Sign Up form. But, I don't use custom templates, so the JavaScript workaround won't work for me.
All I need is to populate the email claim with the value passed in {OIDC:LoginHint}. Is there any way to solve this in the policy XML?
Thank you
In a selfAsserted technical profile, you must:
The IncludeClaimResolvingInClaimsHandling metadata must be set to true.
The input or output claims attribute AlwaysUseDefaultValue must be set to true.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/claim-resolver-overview#using-claim-resolvers
An example of using both settings is here
https://learn.microsoft.com/en-us/azure/active-directory-b2c/claim-resolver-overview#restful-technical-profile

How to do a two way synchronization of user credential (Microsoft AD B2C) with a proprietary database

We need to see the feasibility of synchronization of user credentials
between the Microsoft AD B2C and proprietary database in real time.
If it is feasible do you have any pointers on 'how to'.
What is the hashing algorithm used in the MS AD B2C credentials? is
there a ay to customize it?
Use a REST API call during the Sign Up and Password Reset authentication journey's to send the captured password claim to your API. Your API can then update your DB to keep it in sync.
You can call your REST API and can send any claims captured in the journey using this guide.
In this case, since you are looking to send a password, you must call your REST API as part of a validation technical profile, instead of directly as an orchestration step.
You can send the plaintext password to your API.
Here is an outline of what you need to configure:
In the sign up technical profile, add a call to your REST API as a validation 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>
...
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
<ValidationTechnicalProfile ReferenceId="REST-SyncPasswordToDB" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
Create the REST API technical profile to send the password to your endpoint:
<TechnicalProfile Id="REST-SyncPasswordToDB">
<DisplayName>REST API call to communicate with Legacy DB</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">https://myApi.com/api/syncPassword</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AllowInsecureAuthInProduction">True</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="email" />
<InputClaim ClaimTypeReferenceId="password" />
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>

Getting access token for an api protected by B2C, using custom policies

I have an api that is protected using ADB2C authentication. I need to call this api via custom policies. I followed the documentation enter link description here and have added the two technical profiles as validation technical profile of a self asserted profile.
I am getting an access token returned by the below technical profile :
<TechnicalProfile Id="SecureREST-AccessToken">
<DisplayName></DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">https://login.microsoftonline.com/{tenant id here}/oauth2/v2.0/token</Item>
<Item Key="AuthenticationType">Basic</Item>
<Item Key="SendClaimsIn">Form</Item>
</Metadata>
<CryptographicKeys>
<Key Id="BasicAuthenticationUsername" StorageReferenceId="B2C_1A_SecureRESTClientId" />
<Key Id="BasicAuthenticationPassword" StorageReferenceId="B2C_1A_SecureRESTClientSecret" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="client_credentials" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="{app id uri for protected resource}/.default" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="bearerToken" PartnerClaimType="access_token" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
And then making the rest api call using below profile :
<TechnicalProfile Id="UserMigrationViaLegacyIdp">
<DisplayName>REST API call to communicate with Legacy IdP</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">
https://99a0a14a6402.ngrok.io/api/Identity/SignUpAsync
</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="SendClaimsIn">Header</Item>
<Item Key="AllowInsecureAuthInProduction">false</Item>
<Item Key="UseClaimAsBearerToken">bearerToken</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="bearerToken"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="phonePresent"/>
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
However, scopes are missing from the returned access token, hence token validation is failing on the api.
Is my call to get access token missing anything?
For the client credentials grant flow, the API permissions must be created as roles (see How to: Add app roles to your application and receive them in the token) and then granted admin consent (see Admin consent button).
As result, the bearer token contains the roles claim, rather than the scp claim.
The API application checks access using this roles claim (see Verify app roles in APIs called by daemon apps).

Azure B2C - Custom policies / Add registration step

I'm trying to create a custom policy that will add additional step during the sign up/in process to requires more information.
I added:
1. New ClaimsProvider
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Self Asserted</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="GetCity">
<DisplayName>Local Account Sign In</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>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="city" Required="true" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
New claim type
city
string
city
TextBox
New output claim in SignupSignIn RP
Additional steps before send claims during the UserJourney - SignUpOrSignIn
After the registration there is no authentication and there is no step for "city" data.
Any idea where is the problem? I already configured app insight, but there is no errors there.

Resources