Check user exists in AD B2C(using custom policy) - azure-ad-b2c

Check user exists in AD B2C(using custom policy) if user exists show login option else(if user doesnt exists) show registration form
I tried with below policy but couldnt make it work
https://github.com/azure-ad-b2c/samples/tree/master/policies/split-email-verification-and-signup
<ClaimsTransformation Id="AssertObjectIdObjectIdNotFoundAreEqual" TransformationMethod="CompareClaims">
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" TransformationClaimType="inputClaim1" />
<InputClaim ClaimTypeReferenceId="objectIdNotFound" TransformationClaimType="inputClaim2" />
</InputClaims>
<InputParameters>
<InputParameter Id="operator" DataType="string" Value="NOT EQUAL" />
<InputParameter Id="ignoreCase" DataType="string" Value="true" />
<!-- <InputParameter Id="stringComparison" DataType="string" Value="ordinalIgnoreCase" /> -->
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="EmailExists" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
I'm trying to compare objectid, so i can have EmailExists claim but it doesnt give true/false based on objectid exists
Any one has idea how to do it, please help

The step “known email” -> the logic should be “does ObjectId Exist”.
In your orchestration steps, read the directory using the given email after code verification step. In the AAD technical profile, use the metadata config to not throw an error if the user is not found.
Now you will either have or not have an objectId.
The subsequent orchestration step should use a precondition using ClaimExists logic against objectId to drive the subsequent logic.
You could even use two subjourneys pivoting off whether objectId exists.

Related

How to fetch only one user from Azure AD B2C using a custom policy with different user matches

I have a validation technical profile that checks if there is an existing user with the same company custom attribute during sign up and returns an error. It works great if there is just one user that matches the company name but throws an error when there are multiple which is possible.
Exception is application insight is:
Only one retrieved principal can be returned.
<TechnicalProfile Id="AAD-CheckDuplicateCompany">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_company" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" DefaultValue="NOTFOUND" AlwaysUseDefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="objectIdNotFound" DefaultValue="NOTFOUND" AlwaysUseDefaultValue="true" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="AssertObjectIdAADUserObjectIdNotFoundAreEqual" />
</OutputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
<ClaimsTransformation Id="AssertObjectIdAADUserObjectIdNotFoundAreEqual" TransformationMethod="AssertStringClaimsAreEqual">
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" TransformationClaimType="inputClaim1" />
<InputClaim ClaimTypeReferenceId="objectIdNotFound" TransformationClaimType="inputClaim2" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringComparison" DataType="string" Value="ordinalIgnoreCase" />
</InputParameters>
</ClaimsTransformation>
AAD-CheckDuplicateCompany is used as a validation technical profile in LocalAccountSignUpWithLogonEmail, so it will not insert the user if there is at least one user that exists with the same company attribute. Is there a way to get just one user match?
Not possible. It’s only supported to use an input claim that uniquely identifies an account.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-technical-profile#inputclaims
You need to make your own REST API call and perform your custom logic there.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-rest-api-claims-exchange?pivots=b2c-custom-policy

Azure B2C: Checking if another claim exists during local account creation

In addition to the usual creation of an account based on whether the e-mail used doesn't currently exist, I would like to know how I can also check if another claim value doesn't exist in Azure Active Directory.
For example, for our application, anyone who creates an account must provide an organization name. Once they signup, they are the owner of their organization group.
Before account creation, I want to check if an organization name is not associated with any other account (we're going to do sign-up via invitation if the owner wants to add people to their organization). If it doesn't exist, then create the account. Otherwise, I want to throw an error and prevent the creation of an account.
After looking through the Azure B2C technical profiles documentation, I would think that modifying AAD-UserWriteUsingLogonEmail would be my best guess.
I've tried two approaches so far. The first approach was including the input claim for the organization name. However, this just freezes the test flow:
<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" />
<InputClaim ClaimTypeReferenceId="extension_organizationName" 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="extension_organizationName" />
</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>
The second approach was something similar, but instead using an <InputClaimsTransformations> to check if an organization name exists via the DoesClaimExist action. When using this approach, I get the following error:
Unable to validate the information provided.
Since I'm new to creating custom policies, is modifying the AAD-UserWriteUsingLogonEmail profile on the right track or do I need a completely different approach?
EDIT:
After following Barbara's links, I was able to get the validation working. However, I'm still having issues trying to prevent an account that is using an organization that is already associated with another account. It seems that using an <InputClaimsTransformations> doesn't really do anything.
The error message Unable to validate the information provided. indicates that you did not configure your custom policy correctly to be able to use custom claims.
Thus you have to follow the documentation:
Get the application properties of the extensions app
Modify your custom policy to include the application properties in the AAD-Common-technical profile
Please follow the steps and try again.

Azure AD B2C SelfAssertedAttributeProvider

There are 4 data URIs (taking out older versions)
urn:com:microsoft:aad:b2c:elements:contract:selfasserted:1.1.0
urn:com:microsoft:aad:b2c:elements:contract:multifactor:1.1.0
urn:com:microsoft:aad:b2c:elements:contract:unifiedssp:1.1.0
urn:com:microsoft:aad:b2c:elements:contract:globalexception:1.1.0
My biggest concern is the first one as it is overloaded, and if I put it in a concrete example of the starter pack’s password-reset journey, the first tech-profile of this journey is LocalAccountDiscoveryUsingEmailAddress
The content-def this tech profile is api.localaccountpasswordreset and the data-URI is obviously urn:com:microsoft:aad:b2c:elements:contract:selfasserted:1.1.0
So while climbing up this ladder from user-journey -- > orch.-step -- > tech-profile -- > content-def. -- > data-URI (where actually B2C prepares its own portion of HTML for the browser),
as we know, the OutputClaims in the SelfAssertedAttributeProvider indicates that these claims need to be sent back by the provider and thus will be sourced from the user. In this profile we have following output-claims.
But it is obvious that this provider will NOT prepare UI-widgets to collect values of objectId OR userPrincipalName OR authenticationSource
So in general who made this decision about which output-claims user will be prompted to fill-in ?
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="strongAuthenticationPhoneNumber" />
</OutputClaims>
Thanks.
Got it Chris !!! Thank you so much.
Just for the benefit of everybody, I overlooked following sentence in the doc.
"If the self-asserted technical profile contains a validation technical profile that outputs the same claim, Azure AD B2C does not present the claim to the user."
Thanks
The output claims for a self-asserted technical profile can be sourced as follows:
Collected from the end user
Defaulted to a fixed value
Returned from a validation technical profile
Returned from a claims transformation
In the following example:
The email, newPassword and reenterPassword claim is collected from the end user
The executed-SelfAsserted-Input claim is defaulted to a fixed value (so the end user isn't prompted for it)
The objectId claim is returned from the AAD-UserWriteUsingLogonEmail validation technical profile (so the end user isn't prompted for it)
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
...
<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" AlwaysUseDefaultValue="true" />
...
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
...
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserWriteUsingLogonEmail">
...
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
...
</OutputClaims>
...
</TechnicalProfile>
The Output claims section of the Define a self-asserted technical profile in an Azure Active Directory B2C custom policy article explains this further.
Also on the same thread, may I ask one more clarification about Self-Asserted tech-profile.
What is the need of CryptographicKeys in this profile ?
for eg., how is this signature-key logically used which I am finding in the starter pack ?
Thanks.

Azure AD B2C Custom Policy - Remove Hyphen only using String.Format

In Azure B2C Custom policy I want to remove hyphen '-' from objectId(GUID) claimtype and assign it to another claimtype.
Eg:
Input : e8023a66-30ed-4e31-a17e-c013081704a0
Ouptut : e8023a6630ed4e31a17ec013081704a0
I have currently tried the following:
<InputParameter Id="stringFormat" DataType="string" Value="00000000000000000000000000000000,{0}" />
<InputParameter Id="stringFormat" DataType="string" Value="{0:################################}" />
<InputParameter Id="stringFormat" DataType="string" Value="{0:#}" />
I am using FormatStringClaim transformationmethod in ClaimsTransformation to achieve the same however the value is getting assigned to the target claimtype with hyphen.
Can you please help me in removing hyphen?
Note:
I cannot use any other String operations like Replace or Split as Azure AD supports only String.Format method.
I am using FormatStringClaim transformationmethod in ClaimsTransformation to achieve the same however the value is getting assigned to the target claimtype with hyphen.
It sounds like you incorrectly configured your Claims Transformation and have assigned your original GUID as both your input and output claims. You need to update the ClaimTypeReferenceId with the claim you want to store your modified GUID in.
Example:
<ClaimsTransformation Id="RemoveHash" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="originalGuid" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="modifiedGuid" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>

Azure AD B2C - "emails" claim in custom policy

I'm looking for a way to add an emails claim (collection of emails) to a custom policy for Azure AD B2C. This application claim is available from the Azure Portal directly but I cannot find a way to implement this in a custom policy which I need to create.
What I want to achieve is to have Azure AD B2C authentication for my WebApp users and Azure AD authentication as custom Authentication Provider for employees so It means I will need to add emails claim twice - for Local accounts and for Azure AD.
I followed this guide to make custom policy so I've added a new ClaimsProvider to TrustFrameworkExtensions.xml file.
When I download Sign Up & Sign In policy created in Azure Portal then I can see the following Output Claim:
<OutputClaim ClaimTypeReferenceId="emails" />
I tried to put that line to my custom policy but it does not return emails claim.
Any ideas?
I couldn't find an answer this either - it looks like the "emails" claim is being returned by a custom OutputClaimsTransformation, the configuration of which isn't available in the samples.
I did find the this answer on SO which helped, but it covers updated the "otherMails" claim for NEW users and I had existing users on the basic policies who I couldn't update in that way.
It seems that emails is being populated by concatenating "otherMails" (in the case of social signups) with the first entry in the "signInNames" array.
I ended up doing the following to get the "emails" claim dynamically created.
Create two new ClaimTypes in TrustFrameworkExtensions.xml
<ClaimType Id="emails">
<DisplayName>Emails</DisplayName>
<DataType>stringCollection</DataType>
<UserHelpText>User's email addresses</UserHelpText>
</ClaimType>
<ClaimType Id="firstOtherMail">
<DisplayName>First Other mail</DisplayName>
<DataType>string</DataType>
<UserHelpText>Other Mail</UserHelpText>
</ClaimType>
Create 3 new ClaimsTransformations in TrustFrameworkExtensions.xml
<ClaimsTransformation Id="GetFirstOtherMail" TransformationMethod="GetSingleItemFromStringCollection">
<InputClaims>
<InputClaim ClaimTypeReferenceId="otherMails" TransformationClaimType="collection" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="firstOtherMail" TransformationClaimType="extractedItem" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="CopyFirstOtherMailToEmail" TransformationMethod="AddItemToStringCollection">
<InputClaims>
<InputClaim ClaimTypeReferenceId="firstOtherMail" TransformationClaimType="item" />
<InputClaim ClaimTypeReferenceId="emails" TransformationClaimType="collection" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="emails" TransformationClaimType="collection" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="CopySignInNamesEmailToEmails" TransformationMethod="AddItemToStringCollection">
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInNames.emailAddress" TransformationClaimType="item" />
<InputClaim ClaimTypeReferenceId="emails" TransformationClaimType="collection" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="emails" TransformationClaimType="collection" />
</OutputClaims>
</ClaimsTransformation>
Create a new TechnicalProfile in TrustFrameworkExtensions.xml:
<!-- The following technical profile is used to create the emails collection after user authenticates. -->
<TechnicalProfile Id="AAD-UserCreateEmailsClaim">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="emails" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetFirstOtherMail"/>
<OutputClaimsTransformation ReferenceId="CopySignInNamesEmailToEmails"/>
<OutputClaimsTransformation ReferenceId="CopyFirstOtherMailToEmail"/>
</OutputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
Add a new OrchestrationStep to the SignUpOrSignIn UserJourney just before the last step (SendClaims) in SignUpOrSignIn
<OrchestrationStep Order="8" Type="ClaimsExchange">
<ClaimsExchanges>
<!-- create the emails claim combining signInNames and otherMails -->
<ClaimsExchange Id="AADUserCreateEmailsClaim" TechnicalProfileReferenceId="AAD-UserCreateEmailsClaim" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="9" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
Edit the PolicyProfile TechnicalProfile and add the OutputClaim:
<OutputClaim ClaimTypeReferenceId="emails" />
I took a much simpler route, and just added the following output claim in the SignInSignUp.xml (I left the existing email output claim in, that anyway gets populated only for social sign-ins)
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email" />

Resources