4
votes

I'm trying to write a custom attribute value (into AAD B2C) during a sign-up with email invitation.

It works well with AAD B2C local accounts.

It doesn't work with Google accounts.

I use a combination of :

Here is the custom attribute that I use :

<ClaimsSchema>
  <ClaimType Id="extension_externalid">
    <DisplayName>My External Application Identifier</DisplayName>
    <DataType>string</DataType>
    <UserHelpText>External Application Identifier</UserHelpText>
    <UserInputType>Readonly</UserInputType>
  </ClaimType>
</ClaimsSchema>

The "google account sign-up with email invitation" UserJourney :

<UserJourney Id="SignUpInvitation">
  <OrchestrationSteps>

    <OrchestrationStep Order="1" Type="GetClaims" CpimIssuerTechnicalProfileReferenceId="IdTokenHint_ExtractClaims" />

    <OrchestrationStep Order="2" Type="ClaimsExchange">
     <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>email</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>        
      <ClaimsExchanges>
        <ClaimsExchange Id="SelfAsserted-Unsolicited" TechnicalProfileReferenceId="SelfAsserted-Unsolicited"/>
      </ClaimsExchanges>
    </OrchestrationStep>

    <OrchestrationStep Order="3" Type="ClaimsExchange">
      <ClaimsExchanges>
        <ClaimsExchange Id="GoogleExchange" TechnicalProfileReferenceId="Google-OAUTH" />
      </ClaimsExchanges>
    </OrchestrationStep>

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

    <OrchestrationStep Order="5" 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>

    <OrchestrationStep Order="6" 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="7" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>objectId</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityIdWithUserAttributes" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <OrchestrationStep Order="8" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer"/>

  </OrchestrationSteps>
  <ClientDefinition ReferenceId="DefaultWeb"/>
</UserJourney>

And the technical profile that should write my custom attribute into AAD B2C but doesn't :

<TechnicalProfile Id="AAD-UserWriteUsingAlternativeSecurityIdWithUserAttributes">
  <Metadata>
    <Item Key="Operation">Write</Item>
    <Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
    <Item Key="UserMessageIfClaimsPrincipalAlreadyExists">You are already registered, please press the back button and sign in instead.</Item>
  </Metadata>
  <IncludeInSso>false</IncludeInSso>
  <InputClaimsTransformations>
    <InputClaimsTransformation ReferenceId="CreateOtherMailsFromEmail" />
  </InputClaimsTransformations>
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="AlternativeSecurityId" PartnerClaimType="alternativeSecurityId" Required="true" />
  </InputClaims>
  <PersistedClaims>
    <PersistedClaim ClaimTypeReferenceId="alternativeSecurityId" />
    <PersistedClaim ClaimTypeReferenceId="userPrincipalName" />
    <PersistedClaim ClaimTypeReferenceId="mailNickName" DefaultValue="unknown" />
    <PersistedClaim ClaimTypeReferenceId="displayName" DefaultValue="unknown" />

    <PersistedClaim ClaimTypeReferenceId="otherMails" />
    <PersistedClaim ClaimTypeReferenceId="givenName" />
    <PersistedClaim ClaimTypeReferenceId="surname" />
    <PersistedClaim ClaimTypeReferenceId="extension_externalid" />
  </PersistedClaims>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="objectId" />
    <OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
    <OutputClaim ClaimTypeReferenceId="otherMails" />
  </OutputClaims>
  <IncludeTechnicalProfile ReferenceId="AAD-Common" />
  <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>

For example, here is the technical profile that works well for local accounts :

<TechnicalProfiles>
<TechnicalProfile Id="AAD-UserWriteUsingLogonEmailWithUserAttributes">
  <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="DisablePasswordExpiration" />

    <!-- Optional claims. -->
    <PersistedClaim ClaimTypeReferenceId="givenName" />
    <PersistedClaim ClaimTypeReferenceId="surname" />
    <PersistedClaim ClaimTypeReferenceId="extension_externalid" />
  </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>

Note that I use an id_token_hint to set the extension_externalid value :

<ClaimsProvider>
  <DisplayName>My ID Token Hint ClaimsProvider</DisplayName>
  <TechnicalProfiles>
    <TechnicalProfile Id="IdTokenHint_ExtractClaims">
      <DisplayName>My ID Token Hint TechnicalProfile</DisplayName>
      <Protocol Name="None" />
      <Metadata>
        <Item Key="METADATA">https://XXXXXXXXXXXXX.azurewebsites.net/.well-known/openid-configuration</Item>
      </Metadata>
      <OutputClaims>
        <!--Sample: Read the claims from the id_token_hint-->
        <OutputClaim ClaimTypeReferenceId="email" />
        <OutputClaim ClaimTypeReferenceId="state" />
        <OutputClaim ClaimTypeReferenceId="extension_externalid" />
      </OutputClaims>
    </TechnicalProfile>
  </TechnicalProfiles>
</ClaimsProvider>

I checked that extension_externalid value is correct in the TechnicalProfile "SelfAsserted-Social" (adding it temporarily as input and output claim).

I expect the provided extension_externalid value to be writen in AAD B2C. So I can access it through API Graph AAD or in the sign-in JWT token.

Any help please?

1
Hi @Matthieu. How do you set the extension_externalid value?Chris Padgett
Hi @ChrisPadgett and thank you for your help. I use an id_token_hint the set the value. And I'm just using the "SignUp with email invitation" example (github.com/azure-ad-b2c/samples/tree/master/policies/invite) to do that. You'll find the corresponding ClaimsProvider in my question that I just edited.Matthieu
@Matthieu Have you followed th step described here: docs.microsoft.com/en-us/azure/active-directory-b2c/….Thomas
Thanks @Thomas. I actually use the built-in "b2c-extensions-app" App Registration. My reason is that, in the built-in sign-in "User Flows", only the custom attributes stored in "b2c-extensions-app" are returned in the authentication token. So I didn't created my own App Registration. However, I think you gave me a really good lead ...Matthieu

1 Answers

2
votes

I think that the problem was in my 5th orchestration step ...

I was using the "SelfAsserted-Social" technical profile (from the starter pack - https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack/blob/ee4832db16a226416eac1de3bd96d4a5eacff790/SocialAndLocalAccounts/TrustFrameworkBase.xml#L761) that doesn't know my "extension_externalid" attribute.

Adding a "extension_externalid" input claim to this technical profile fixed the problem.

<TechnicalProfile Id="SelfAsserted-Social">
  <DisplayName>User ID 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.selfasserted</Item>
  </Metadata>
  <CryptographicKeys>
    <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
  </CryptographicKeys>
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="displayName" />
    <InputClaim ClaimTypeReferenceId="givenName" />
    <InputClaim ClaimTypeReferenceId="surname" />
    <InputClaim ClaimTypeReferenceId="extension_externalid" />
  </InputClaims>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="objectId" />
    <OutputClaim ClaimTypeReferenceId="newUser" />
    <OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
    <OutputClaim ClaimTypeReferenceId="displayName" />
    <OutputClaim ClaimTypeReferenceId="givenName" />
    <OutputClaim ClaimTypeReferenceId="surname" />
  </OutputClaims>
  <ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingAlternativeSecurityIdWithUserAttributes" />
  </ValidationTechnicalProfiles>
  <UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialSignup" />
</TechnicalProfile>

Thanks to @Thomas and @ChrisPadgett for their great help !!