10
votes

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?

2
Where are you trying to get the email address from before putting it in the token? Is it a Rest API, or just a local account sign-in? If you have a new technical profile, then it will be good to add that in the question. Otherwise, mention the starter pack and your scenario.Omer Iqbal
This post doesn't work, this one does.ton.yeung

2 Answers

10
votes

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" />
7
votes

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" />