Azure AD B2C Claim resolver: Read user details using objectId from query string - azure-active-directory

I have to prepopulate user email id in the user journey using the object id passed as query string in authorize URL.
I referred to non protocol parameter claim resolvers
But i am getting exception when i try to read using ObjectId.
TechnicalProfile,
<TechnicalProfile Id="AAD-UserReadUsingObjectIdNew">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" DefaultValue="{OAUTH-KV:objId}"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="signInName" />
<!-- Optional claims -->
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="otherMails" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="country" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="extension_creationsource" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
UserJourney,
<UserJourney Id="PasswordChange">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountRead" TechnicalProfileReferenceId="AAD-UserReadUsingObjectIdNew" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountLogin" TechnicalProfileReferenceId="LocalAccountLoginUsingObjectIdAndPassword" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
URL,
https://tenant.b2clogin.com/tenant.onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1A_PasswordChange_Mob_HH_NewFlow&client_id=xxxxxx-2415-4bb9-9529-131edxxxxx&nonce=defaultNonce&redirect_uri=https%3A%2F%2Flocalhost%3A44321%2F&scope=openid&response_type=id_token&prompt=login&objId=xxxxxx-5e54-4126-9982-bexxxxxx
AppInsight,
"Statebag": {
"Complex-CLMS": {
"objectId": "{OAUTH-KV:objId}"
}
"Message": "An error occurred while retrieving User using identifier claim type \"objectId\" in tenant \"4722b4c2-6388-4c69-9cfb-f950ea762665\". Error returned was 400/Request_BadRequest: Bad request. Please fix the request before retrying."
Notes:
If i make a Rest API call and sending this ObjectId as inputclaim, It is resolving the value and I am able to get the object id value in API request body properly. But if I use it against to read from AAD, it is not resolving the value
Update:
I was trying to check with login hint works as per Link
But it didn't work for me. Then I used signinorsignup policy from startpack.
It started working for signinorsignup. So I found that,
{OIDC:LoginHint} works in only orchestration step CombinedSignInAndSignUp. It does not work in ClaimsProviderSelection.
I think that is why {OAUTH-KV:objId} is also not working for me as am using ClaimsProviderSelection
Any help will be appreciated.
Please help me to fix this.

Related

How to get custom B2C policy to send a oauth2 bearer (token) to my custom SignUp/SignIn API

I am trying to have my custom B2C policy to communicate with my custom SignUp/SignIn API and authenticate via oauth2 bearer (not static bearer). I have followed the instructions found here: 1 (to the letter) but unable to get it working. I cannot get the custom policy to send the Authorization header with a bearer token to my API, unsure why.
Here is my configuration:
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<!--OAUTH2.0 customization START-->
<TechnicalProfile Id="REST-AcquireAccessToken">
<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/mytenant.onmicrosoft.com/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="https://graph.microsoft.com/.default" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="bearerToken" PartnerClaimType="access_token" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<!--OAUTH2.0 customization END-->
<TechnicalProfile Id="REST-GetProfile">
<DisplayName>Get user extended profile Azure Function web hook</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">https://url_to_API/api/SignIn?</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="UseClaimAsBearerToken">bearerToken</Item>
<Item Key="AllowInsecureAuthInProduction">false</Item>
</Metadata>
<InputClaims>
<!-- Claims sent to your REST API -->
<InputClaim ClaimTypeReferenceId="bearerToken"/>
<InputClaim ClaimTypeReferenceId="objectId" />
<InputClaim ClaimTypeReferenceId="extension_MemberId" />
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<!-- Claims parsed from your REST API -->
<OutputClaim ClaimTypeReferenceId="extension_MemberId" PartnerClaimType="extension_MemberId"/>
<OutputClaim ClaimTypeReferenceId="email" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
Something that was not mentioned in the documentation, but saw on a post, is you need to add an orchestration step in the signup/signin journey to call the "get an access token" - but not 100% sure it needs it.
<OrchestrationStep Order="5" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTGetAccessToken" TechnicalProfileReferenceId="REST-AcquireAccessToken" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTGetProfile" TechnicalProfileReferenceId="REST-GetProfile" />
</ClaimsExchanges>
</OrchestrationStep>
I checked my API without Authorization configured and can see the custom B2C policy is calling it, but I can't see any evidence of a bearer token at all (looked at the Request.Headers in the c# code). Also, I am unsure how to configure my API Authorization side of things, that was also omitted in the documentation (unfortunately).
With the above, am I missing something? Should I see the Authorization header with a bearer token?
Any help is very much appreciated!

how to get OAuth2 bearer access token in b2c custom policy to send claims to My REST API during signin sign up custom policy

i have b2c local account sign in sign up flow using custom policies to sign in sign up
i want to add a call to my secure web API to send user claims to this API. i have followed this documentation Secure your API and access through custom policy and using OAuth2 bearer authentication. for this i have registered another application as the above documentation says, but i am not getting any bearer token in bearer token claim(access token).
i am getting this following specific error(in jwt.ms) when running my policy
error:Claim "bearerToken" specified for the bearer token is not present in the available claims.
but when i add the Technical profile "REST-AcquireAccessToken" in user journey i can see it is returning the access token in bearerToken Claim.
but when i add Technical Profile "REST-API-SignUp" in user journey i got the following error :
do i need to call both the REST technical profiles (REST-AcquireAccessToken,REST-API-SignUp) in user journey steps??
i tried doing so but it faild to upload the policy.
any help will be much appreciated.
these are the Technical Profiles i used first for acquiring the Access Token and second one to call API to send user claims
<TechnicalProfile Id="REST-AcquireAccessToken">
<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/MyTenantName.onmicrosoft.com/oauth2/v2.0/token</Item>
<Item Key="AuthenticationType">Basic</Item>
<Item Key="SendClaimsIn">Form</Item>
</Metadata>
<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="https://MyTenantName.onmicrosoft.com/api/.default" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="bearerToken" PartnerClaimType="access_token" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<!--talking To API-->
<TechnicalProfile Id="REST-API-SignUp">
<DisplayName>Send account details to API</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 My REST API endpoint -->
<Item Key="ServiceUrl">https://My API endPoint URL</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="UseClaimAsBearerToken">bearerToken</Item>
<Item Key="AllowInsecureAuthInProduction">false</Item>
</Metadata>
<InputClaims>
<!-- Claims Needs to sent to My REST API -->
<InputClaim ClaimTypeReferenceId="bearerToken" />
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="givenName" />
<InputClaim ClaimTypeReferenceId="surname" />
<InputClaim ClaimTypeReferenceId="displayName" />
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
In user Journey i am calling the Technical Profile "REST-API-SignUp" in step 8 like this:
<OrchestrationStep Order="8" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTPostUserClaims" TechnicalProfileReferenceId="REST-API-SignUp" />
</ClaimsExchanges>
</OrchestrationStep>
do i need to call TechnicalProfile "REST-AcquireAccessToken" also in user journet step?if yes then when?
Thanks for the update #Munazza Osama , Posting the solution in the answer .
Solution:
The error:Claim "bearerToken" specified for the bearer token is not present in the available claims, got resolved by
Calling the "REST-AcquireAccessToken" Technical Profile to get access token along with calling "REST-API-SignUp" technical profile in User journey.
<OrchestrationStep Order="8" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTGetAccessToken" TechnicalProfileReferenceId="REST-AcquireAccessToken " />
<ClaimsExchange Id="RESTPostUserClaims" TechnicalProfileReferenceId="REST-API-SignUp" />
</ClaimsExchanges>
</OrchestrationStep>

In Azure AD B2C, How do i link a social account with any existing local account during first time sign in from social login?

When signing in with a social account, Facebook for now, I want to check if there is a local account already - using custom policies. If so the user must sign in to that so it can be linked to the social account, and failure to do so aborts social login.
If there is no local account already just create the social account and a local account with a random password as explained here.
There is already a stackoverflow question which is fairly similar here, but it always requires signing in to a local account which I do not want, and talks about "SQL identity service" which means nothing to me.
I managed to get this working using the TechnicalProfile from wojtekdo as a starting point and carefully reading the MS documents on custom profiles with an eye to what I was trying to do. I also took the concept of userIdentities from the link provided in the comment by Jas Suri.
I did the merging of the social account with the local account using a subjourney simply to keep it separate from the main journey.
Note that I do not verify or ask for password for the local account. This I think acceptable since the user has already verified ownership of the email when creating the facebook account. Nevertheless, I would prefer to verify the local account and I have a Stackoverflow question on how to do this.
TrustFrameworkExtensions xml to achieve this:
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="userIdentity">
<DisplayName>userIdentity</DisplayName>
<DataType>userIdentity</DataType>
<AdminHelpText>userIdentity</AdminHelpText>
<UserHelpText>userIdentity</UserHelpText>
</ClaimType>
<ClaimType Id="userIdentities">
<DisplayName>userIdentities</DisplayName>
<DataType>userIdentityCollection</DataType>
<AdminHelpText>userIdentities</AdminHelpText>
<UserHelpText>userIdentities</UserHelpText>
</ClaimType>
<ClaimType Id="issuers">
<DisplayName>issuers</DisplayName>
<DataType>stringCollection</DataType>
<UserHelpText>User identity providers. This information is received from alternativeSecurityIds</UserHelpText>
</ClaimType>
<ClaimType Id="signInNamesInfo.emailAddress">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<AdminHelpText>Email address that the user can use to sign in.</AdminHelpText>
<UserHelpText>Email address to use for signing in.</UserHelpText>
<UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="emails">
<DisplayName>Email Addresses</DisplayName>
<DataType>stringCollection</DataType>
<AdminHelpText>Email addresses of the user.</AdminHelpText>
<UserHelpText>Your email addresses.</UserHelpText>
</ClaimType>
<ClaimType Id="strongAuthenticationEmailAddress">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<AdminHelpText>Email address that the user can use for strong authentication.</AdminHelpText>
<UserHelpText>Email address to use for strong authentication.</UserHelpText>
<UserInputType>TextBox</UserInputType>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<ClaimsTransformation Id="CreateEmailsFromOtherMailsAndSignInNamesInfo" TransformationMethod="AddItemToStringCollection">
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInNamesInfo.emailAddress" TransformationClaimType="item" />
<InputClaim ClaimTypeReferenceId="otherMails" TransformationClaimType="collection" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="emails" TransformationClaimType="collection" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="AddStrongAuthenticationEmailToEmails" TransformationMethod="AddItemToStringCollection">
<InputClaims>
<InputClaim ClaimTypeReferenceId="strongAuthenticationEmailAddress" TransformationClaimType="item" />
<InputClaim ClaimTypeReferenceId="emails" TransformationClaimType="collection" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="emails" TransformationClaimType="collection" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="CreateSubjectClaimFromObjectID" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="Not supported currently. Use oid claim." />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="sub" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
<!-- Sample: On sign-in (first time) with social account, create a userIdentity claim, using issuerUserId and issuer name -->
<ClaimsTransformation Id="CreateUserIdentity" TransformationMethod="CreateUserIdentity">
<InputClaims>
<InputClaim ClaimTypeReferenceId="issuerUserId" TransformationClaimType="issuerUserId" />
<InputClaim ClaimTypeReferenceId="identityProvider" TransformationClaimType="issuer" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="userIdentity" TransformationClaimType="userIdentity" />
</OutputClaims>
</ClaimsTransformation>
<!--Sample: Add a userIdentity to the userIdentities collection. .-->
<ClaimsTransformation Id="AppendUserIdentity" TransformationMethod="AddItemToUserIdentityCollection">
<InputClaims>
<InputClaim ClaimTypeReferenceId="userIdentity" TransformationClaimType="item" />
<InputClaim ClaimTypeReferenceId="userIdentities" TransformationClaimType="collection" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="userIdentities" TransformationClaimType="collection" />
</OutputClaims>
</ClaimsTransformation>
<!--Sample: Extracts the list of social identity providers associated with the user -->
<ClaimsTransformation Id="ExtractIssuers" TransformationMethod="GetIssuersFromUserIdentityCollectionTransformation">
<InputClaims>
<InputClaim ClaimTypeReferenceId="userIdentities" TransformationClaimType="userIdentityCollection" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuers" TransformationClaimType="issuersCollection" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-ReadCommon">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="otherMails" />
<OutputClaim ClaimTypeReferenceId="strongAuthenticationEmailAddress" PartnerClaimType="signInNames.emailAddress" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateEmailsFromOtherMailsAndSignInNamesInfo" />
<OutputClaimsTransformation ReferenceId="AddStrongAuthenticationEmailToEmails" />
</OutputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserReadUsingEmailAddress-NoError">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided user ID.</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="logonIdentifier" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="accountEnabled" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-ReadCommon" />
</TechnicalProfile>
<TechnicalProfile Id="AAD-AssertAccountEnabledAndCreateSubjectClaimFromObjectId">
<DisplayName>Assert account enabled</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="accountEnabled" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="email" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="AssertAccountEnabledIsTrue" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromObjectID" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserUpdateWithUserIdentities">
<Metadata>
<Item Key="api-version">1.6</Item>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId" />
<PersistedClaim ClaimTypeReferenceId="userIdentities" />
<!--<PersistedClaim ClaimTypeReferenceId="extension_requiresMigrationBool" DefaultValue="false" AlwaysUseDefaultValue="true"/>-->
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="userIdentities" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="ExtractIssuers" />
</OutputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Facebook</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Facebook-OAUTH">
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateUserIdentity" />
<OutputClaimsTransformation ReferenceId="AppendUserIdentity" />
</OutputClaimsTransformations>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignUpOrSignIn">
<OrchestrationSteps>
<!-- For social IDP authentication, attempt to find the user account in the directory. -->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>localAccountAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError"/>
</ClaimsExchanges>
</OrchestrationStep>
<!-- Find local account using email-->
<OrchestrationStep Order="5" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>localAccountAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="FindLocalAccount" TechnicalProfileReferenceId="AAD-UserReadUsingEmailAddress-NoError"/>
</ClaimsExchanges>
</OrchestrationStep>
<!-- start a subjourney to verify local account if one was found in previous step -->
<OrchestrationStep Order="6" Type="InvokeSubJourney">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>localAccountAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<JourneyList>
<Candidate SubJourneyReferenceId="MergeWithLocalAccount" />
</JourneyList>
</OrchestrationStep>
</OrchestrationSteps>
</UserJourney>
</UserJourneys>
<SubJourneys>
<SubJourney Id="MergeWithLocalAccount" Type="Call">
<OrchestrationSteps>
<!-- assert any found local account is enabled -->
<OrchestrationStep Order="1" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>localAccountAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AssertLocalAccountEnabled" TechnicalProfileReferenceId="AAD-AssertAccountEnabledAndCreateSubjectClaimFromObjectId"/>
</ClaimsExchanges>
</OrchestrationStep>
<!-- merge account with any existing and verified local account-->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>localAccountAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>accountVerified</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADMergeAccount" TechnicalProfileReferenceId="AAD-UserUpdateWithUserIdentities" />
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</SubJourney>
</SubJourneys>
I have included all the xml I added to solve the merging social and local account, excluding everything else. I am aware this is not a minimal solution and some bits are probably not needed, but it is a working solution and might help others figure it out.
Assuming you are using email address as the user name you need to get that email address every time you sign in to that social provider. When you finalize the extenal provider exchange (be it Facebook) you need to take the received email address and make use of a technical profile which allows reading directory by email. There is a profile which does it but it throws an error when the account is not found so you need a slightly modified version, namely with RaiseErrorIfClaimsPrincipalDoesNotExist key set to false:
<TechnicalProfile Id="AAD-UserReadUsingEmailAddress-NoError">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided user ID.</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="logonIdentifier" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="accountEnabled" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="AssertAccountEnabledIsTrue" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromObjectID" />
</OutputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="AAD-ReadCommon" />
</TechnicalProfile>
After executing the profile you will know, by checking ObjectId, if the user was found or not and act accordingly.

Custom policy ends with an error 500 on oauth2/authresp

I want to have 2 login providers for my app. Customers would connect with B2C and employees woudl connect with our AAD by SSO. Currently the B2C login for customers works with a SignIn V2 user flow, and our SSO works just fine for any other applications.
I followed these 2 pages to get started, using the exact same names:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-get-started-custom
https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-setup-aad-custom
They could be clearer but I think I got everything right as far as the XML goes. When I run my custom policy, I get a page with a login form and a button to connect with the AD. If I click the button, I'm redirected to the SSO page and I log in with my user. The first time I'm asked to accept the permissions. So far so good, but after that I get redirected to https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/oauth2/authresp, which gives a generic error 500 page. In the B2C Audit log, I see an event "Federate with an identity provider" with "Status: success" for the same datetime as my login so I believe the login works. Similarly, I can see a successful sign-in in the user's page in the AAD.
Is there something more I need to do that the MSDN pages missed? I should be getting redirected to jwt.ms with a token.
Relevant xml files (redacted):
TrustFrameworkBase.xml (only the parts I've modified):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<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="mytenant.onmicrosoft.com"
PolicyId="B2C_1A_TrustFrameworkBase"
PublicPolicyUri="http://mytenant.onmicrosoft.com/B2C_1A_TrustFrameworkBase">
<!-- snip default building blocks -->
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<DisplayName>Local Account SignIn</DisplayName>
<Protocol Name="OpenIdConnect" />
<Metadata>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">We can't seem to find your account</Item>
<Item Key="UserMessageIfInvalidPassword">Your password is incorrect</Item>
<Item Key="UserMessageIfOldPasswordUsed">Looks like you used an old password</Item>
<Item Key="ProviderName">https://sts.windows.net/mytenantguid/</Item>
<Item Key="METADATA">https://login.microsoftonline.com/mytenant.onmicrosoft.com/.well-known/openid-configuration</Item>
<Item Key="authorization_endpoint">https://login.microsoftonline.com/mytenant.onmicrosoft.com/oauth2/token</Item>
<Item Key="response_types">id_token</Item>
<Item Key="response_mode">query</Item>
<Item Key="scope">email openid</Item>
<!-- Policy Engine Clients -->
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="HttpBinding">POST</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="username" Required="true" />
<InputClaim ClaimTypeReferenceId="password" Required="true" />
<InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="password" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid" />
<InputClaim ClaimTypeReferenceId="nca" PartnerClaimType="nca" DefaultValue="1" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="oid" />
<OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" PartnerClaimType="upn" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- snip other default claim providers-->
<!-- snip default user journeys-->
</TrustFrameworkPolicy>
TrustFrameworkExtension.xml
<?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="mytenant.onmicrosoft.com"
PolicyId="B2C_1A_TrustFrameworkExtensions"
PublicPolicyUri="http://mytenant.onmicrosoft.com/B2C_1A_TrustFrameworkExtensions">
<BasePolicy>
<TenantId>mytenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkBase</PolicyId>
</BasePolicy>
<BuildingBlocks>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">ProxyIdentityExperienceFramework_AppId</Item>
<Item Key="IdTokenAudience">IdentityExperienceFramework_AppId</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="ProxyIdentityExperienceFramework_AppId" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="IdentityExperienceFramework_AppId" />
</InputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<Domain>Mycompany</Domain>
<DisplayName>Login using Mycompany</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="MycompanyProfile">
<DisplayName>Mycompany Employee</DisplayName>
<Description>Login with your Mycompany account</Description>
<Protocol Name="OpenIdConnect"/>
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="METADATA">https://login.windows.net/mytenant.onmicrosoft.com/.well-known/openid-configuration</Item>
<Item Key="ProviderName">https://sts.windows.net/mytenantguid/</Item>
<Item Key="client_id">AzureADB2CApp_AppdId</Item>
<Item Key="IdTokenAudience">AzureADB2CApp_AppdId</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="response_types">code</Item>
<Item Key="scope">openid</Item>
<Item Key="response_mode">form_post</Item>
<Item Key="HttpBinding">POST</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_MycompanySecret"/>
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="oid"/>
<OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid"/>
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
</OutputClaimsTransformations>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignUpOrSignInMycompany">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signinwithpassword">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="MycompanyExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Check if the user has selected to sign in using one of the social providers -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
<ClaimsExchange Id="MycompanyExchange" TechnicalProfileReferenceId="MycompanyProfile" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Show self-asserted page only if the directory does not have the user account already (i.e. we do not have an objectId).
This can only happen when authentication happened using a social IDP. If local account was created or authentication done
using ESTS in step 2, then an user account must exist in the directory by this time. -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-Social" TechnicalProfileReferenceId="SelfAsserted-Social" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- This step reads any user attributes that we may not have received when authenticating using ESTS so they can be sent
in the token. -->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>socialIdpAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
</TrustFrameworkPolicy>
SignUpOrSigninMycompany.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<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="mytenant.onmicrosoft.com"
PolicyId="B2C_1A_signup_signin_mycompany"
PublicPolicyUri="http://mytenant.onmicrosoft.com/B2C_1A_signup_signin_mycompany">
<BasePolicy>
<TenantId>mytenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignInMycompany" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
In the Identity Experience Framework panel, I run B2C_1A_signup_signin_mycompany and choose testapp1 with https://jwt.ms as the repyl url.
After reaching out for support on MSDN, which didn't help, I've redone the whole process from scratch and I get past this error though I still end up with an error :
AADB2C90037: An error occurred while processing the request. Please contact administrator of the site you are trying to access.
Which was caused by a missing outputclaim for the objectid.
What solved the problem of this question was 2 misunderstandings on my part from unclear instructions (imo).
The starter pack policies have placeholders you need to replace. Some fields look like placeholders but aren't and I had replaced them.
The claim provider given at step 3 contains transformations and OutputClaim that aren't defined in TrustFrameworkBase.xml. I had simply removed them at first but this time I found their definition on MSDN and added them.

Getting email after logged in with Azure AD on Azure AD B2C

Scenario: I'm using Angular 5 for front-end and .NET core 2.0 for back-end, MSAL.js to authenticate against Azure AD B2C in Angular SPA, then use returned id_token as Bearer Token to send requests to WebAPI endpoints.
I have successfully setup multi-tenant Azure AD as a provider in Azure AD B2C (followed the answer here Multi-tenant Azure AD in Azure AD B2C), but in the returned id_token, there's no claim for email address. Note: If I configure single-tenant Azure AD, I get back a claim with type http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress for email address, but wasn't able to do so with multi-tenant AD.
I believe the limitation is with Azure AD v2.0 that's been mentioned here: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-limitations
Question: How do I retrieve the user's email address after log in.
I followed the guideline in this article https://monteledwards.com/2017/10/18/a-complete-integration-azure-ad-b2c-azure-ad-graph-api-logic-apps/ to add an extra logic app to resolve email from id_token, but my problem is I don't have objectId back either.
Claims I've got back after successful authentication are:
iss - https://login.microsoftonline.com/<My-B2C-Tenant-Id>/v2.0/
exp - ticks
nbf - ticks
aud - My-B2C-App-Id
name - string
http://schemas.microsoft.com/identity/claims/identityprovider - tid
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier - My-B2C-App-Id
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname - string
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname - string
nonce - GUID
http://schemas.microsoft.com/identity/claims/scope - User.Read
azp - GUID
ver - 1.0
iat - ticks
My technical profile for multi-tenant Azure AD -> Azure AD B2C is:
<TechnicalProfile Id="AzureADAccountProfile">
<DisplayName>Log in with your work 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 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</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_AzureADSecret"/>
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="enterpriseAuthentication" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="tid" />
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="oid" />
<OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid"/>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="unique_name" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
<OutputClaim ClaimTypeReferenceId="tenant" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
Getting the email address that is associated with the user account differs between a consumer/personal account and an organization/work account.
Personal account
Reference: Azure Active Directory v2.0 tokens reference
The email address that is associated with the user account can be issued in the ID token.
1) Change the "scope" metadata item from "openid profile" to "openid profile email".
<Metadata>
<Item Key="scope">openid profile email</Item>
</Metadata>
2) Change the "email" output claim from:
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="unique_name" />
to:
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
Work account
The email address that is associated with the user account must be retrieved using the Microsoft Graph API.
1) Change the "AzureADAccountProfile" technical profile from "OpenIdConnect" to "OAuth2" and add the metadata items to retrieve the profile properties for the signed-in user.
Note: The "Get a user" operation doesn't return the tenant identifier of the signed-in user so the following technical profile creates the "identityProvider" claim, which is required for the alternative security identifier, from the domain part of the "userPrincipalName" property of this user.
<TechnicalProfile Id="AzureADAccountProfile">
<DisplayName>Log in with your work account</DisplayName>
<Protocol Name="OAuth2"/>
<OutputTokenFormat>JWT</OutputTokenFormat>
<Metadata>
<Item Key="AccessTokenEndpoint">https://login.microsoftonline.com/organizations/oauth2/v2.0/token</Item>
<Item Key="authorization_endpoint">https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize</Item>
<Item Key="BearerTokenTransmissionMethod">AuthorizationHeader</Item>
<Item Key="ClaimsEndpoint">https://graph.microsoft.com/v1.0/me</Item>
<Item Key="client_id"><!-- Enter your client ID --></Item>
<Item Key="DiscoverMetadataByTokenIssuer">true</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="IdTokenAudience"><!-- Enter your client ID --></Item>
<Item Key="response_types">code</Item>
<Item Key="scope">https://graph.microsoft.com/user.read</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_AzureADSecret"/>
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="enterpriseAuthentication" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="displayName" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="mail" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="surname" />
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="id" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" PartnerClaimType="userPrincipalName" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateAzureADIdentityProvider" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
2) Create the "CreateAzureADIdentityProvider" claims transformation.
<ClaimsTransformation Id="CreateAzureADIdentityProvider" TransformationMethod="ParseDomain">
<InputClaims>
<InputClaim ClaimTypeReferenceId="userPrincipalName" TransformationClaimType="emailAddress" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="identityProvider" TransformationClaimType="domain" />
</OutputClaims>
</ClaimsTransformation>

Resources