Home > Net >  Validation errors when trying to include custom claims in access token
Validation errors when trying to include custom claims in access token

Time:05-31

I am struggling a lot with a custom policy for Azure AD B2C. I have based my policies on the jit-migration-v2 files: https://github.com/azure-ad-b2c/user-migration/tree/master/jit-migration-v2

I added 4 new custom attributes and included them in the TrustFrameworkExtension.xml file: extension_creditorId, extension_likvidoUserId, extension_likvidoRole & extension_title.

Here is my complete TrustFrameworkExtension.xml file, where you can see that I added these attributes as output claims and persisted claims:

<?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="{Settings:Tenant}" PolicyId="B2C_1A_JITMigraion_TrustFrameworkExtensions" PublicPolicyUri="http://{Settings:Tenant}/B2C_1A_JITMigraion_TrustFrameworkExtensions">

  <BasePolicy>
    <TenantId>{Settings:Tenant}</TenantId>
    <PolicyId>B2C_1A_JITMigraion_TrustFrameworkBase</PolicyId>
  </BasePolicy>
  <BuildingBlocks>
    <ClaimsSchema>
      <!--Demo: This claim indicates whether the user need to migrate-->
      <ClaimType Id="needToMigrate">
        <DisplayName>needToMigrate</DisplayName>
        <DataType>string</DataType>
        <AdminHelpText>Indicates whether the user need to migrate</AdminHelpText>
        <UserHelpText>Indicates whether the user need to migrate</UserHelpText>
      </ClaimType>
      <ClaimType Id="useInputPassword">
        <DisplayName>useInputPassword</DisplayName>
        <DataType>boolean</DataType>
      </ClaimType>

      <ClaimType Id="extension_creditorId">
        <DisplayName>Creditor ID</DisplayName>
        <DataType>int</DataType>
        <AdminHelpText>The ID of the creditor this user belongs to</AdminHelpText>
        <UserHelpText>The ID of the creditor this user belongs to</UserHelpText>
      </ClaimType>
      <ClaimType Id="extension_likvidoUserId">
        <DisplayName>Likvido user ID</DisplayName>
        <DataType>string</DataType>
        <AdminHelpText>The ID of the user in the Likvido DB</AdminHelpText>
        <UserHelpText>The ID of the user in the Likvido DB</UserHelpText>
      </ClaimType>
      <ClaimType Id="extension_likvidoRole">
        <DisplayName>Likvido user role</DisplayName>
        <DataType>string</DataType>
        <AdminHelpText>The role of the user in Likvido</AdminHelpText>
        <UserHelpText>The role of the user in Likvido</UserHelpText>
      </ClaimType>
      <ClaimType Id="extension_title">
        <DisplayName>Likvido user title</DisplayName>
        <DataType>string</DataType>
        <AdminHelpText>The title of the user</AdminHelpText>
        <UserHelpText>The title of the user</UserHelpText>
      </ClaimType>
    </ClaimsSchema>
  </BuildingBlocks>

  <ClaimsProviders>
    <ClaimsProvider>
      <DisplayName>Azure Active Directory</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="AAD-Common">
          <Metadata>
            <!--Insert b2c-extensions-app application ID here, for example: 11111111-1111-1111-1111-111111111111-->  
            <Item Key="ClientId">{Settings:B2CExtensionsAppClientId}</Item>
            <!--Insert b2c-extensions-app application ObjectId here, for example: 22222222-2222-2222-2222-222222222222-->
            <Item Key="ApplicationObjectId">{Settings:B2CExtensionsAppObjectId}</Item>
          </Metadata>
        </TechnicalProfile>
      </TechnicalProfiles> 
    </ClaimsProvider>
    <!-- Local account Sign-Up claims provider -->
    <ClaimsProvider>
      <DisplayName>Local Account</DisplayName>
      <TechnicalProfiles>

        <!-- SIGN-IN -->
        <TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="needToMigrate" />
          </OutputClaims>
          <ValidationTechnicalProfiles>
            <!--Demo: Add user migration validation technical profile before login-NonInteractive -->
            <ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-SignIn" ContinueOnError="false" />

            <!--Demo: Run this validation technical profile only if user doesn't need to migrate -->
            <ValidationTechnicalProfile ReferenceId="login-NonInteractive">
              <Preconditions>
                <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
                  <Value>needToMigrate</Value>
                  <Value>local</Value>
                  <Action>SkipThisValidationTechnicalProfile</Action>
                </Precondition>
              </Preconditions>
            </ValidationTechnicalProfile>

            <!--Demo: Run this validation technical profile only if user needs to migrate -->
            <ValidationTechnicalProfile ReferenceId="AAD-MigrateUserUsingLogonEmail">
              <Preconditions>
                <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
                  <Value>needToMigrate</Value>
                  <Value>local</Value>
                  <Action>SkipThisValidationTechnicalProfile</Action>
                </Precondition>
              </Preconditions>
            </ValidationTechnicalProfile>

          </ValidationTechnicalProfiles>
        </TechnicalProfile>

        <!-- SIGN-UP -->
        <TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
          <Metadata>
            <Item Key="EnforceEmailVerification">False</Item>
          </Metadata>
          <ValidationTechnicalProfiles>
            <!--Demo: Add user migration validation technical profile before AAD-UserWriteUsingLogonEmail -->
            <ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-SignUp" ContinueOnError="false" />
            <ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" ContinueOnError="false" />
          </ValidationTechnicalProfiles>
        </TechnicalProfile>

        <!-- PASSWORD RESET first page -->
        <TechnicalProfile Id="LocalAccountDiscoveryUsingEmailAddress">
          <Metadata>
            <Item Key="EnforceEmailVerification">False</Item>
          </Metadata>
          <ValidationTechnicalProfiles>
            <ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" ContinueOnError="true" />
            <ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-PasswordReset1" ContinueOnError="false" />
          </ValidationTechnicalProfiles>
        </TechnicalProfile>

        <!-- PASSWORD RESET second page -->
        <TechnicalProfile Id="LocalAccountWritePasswordUsingObjectId">
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="objectId" />
            <OutputClaim ClaimTypeReferenceId="authenticationSource" />
          </OutputClaims>
          <ValidationTechnicalProfiles>
            <ValidationTechnicalProfile ReferenceId="AAD-UserWritePasswordUsingObjectId">
              <!--Don't run this validation technical profile if objectId is not exists (migrated acccount)-->
              <Preconditions>
                <Precondition Type="ClaimsExist" ExecuteActionsIf="false">
                  <Value>objectId</Value>
                  <Action>SkipThisValidationTechnicalProfile</Action>
                </Precondition>
              </Preconditions>
            </ValidationTechnicalProfile>

            <ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-PasswordReset2">
              <!--Don't run this validation technical profile if objectId is exists (existing acccount)-->
              <Preconditions>
                <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
                  <Value>objectId</Value>
                  <Action>SkipThisValidationTechnicalProfile</Action>
                </Precondition>
              </Preconditions>
            </ValidationTechnicalProfile>

            <ValidationTechnicalProfile ReferenceId="AAD-MigrateUserUsingLogonEmail">
              <!--Don't run this validation technical profile if objectId is exists (existing acccount)-->
              <Preconditions>
                <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
                  <Value>objectId</Value>
                  <Action>SkipThisValidationTechnicalProfile</Action>
                </Precondition>
              </Preconditions>
            </ValidationTechnicalProfile>

          </ValidationTechnicalProfiles>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <ClaimsProvider>
      <DisplayName>REST APIs</DisplayName>
      <TechnicalProfiles>

        <!--Demo: Checks if user exists in the migration table. If yes, validate the credentials and migrate the account -->
        <TechnicalProfile Id="REST-UserMigration-LocalAccount-SignIn">
          <DisplayName>Migrate user sign-in flow</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/migrate</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">Body</Item>
            <Item Key="AllowInsecureAuthInProduction">True</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="signInName" />
            <InputClaim ClaimTypeReferenceId="password" />
            <InputClaim ClaimTypeReferenceId="useInputPassword" DefaultValue="false" />
          </InputClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="needToMigrate" />
            <OutputClaim ClaimTypeReferenceId="email" />
            <OutputClaim ClaimTypeReferenceId="newPassword" />
            <OutputClaim ClaimTypeReferenceId="displayName" />
            <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="firstName" />
            <OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="lastName" />
            <OutputClaim ClaimTypeReferenceId="extension_creditorId" PartnerClaimType="creditorId" />
            <OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" PartnerClaimType="likvidoUserId" />
            <OutputClaim ClaimTypeReferenceId="extension_likvidoRole" PartnerClaimType="likvidoRole" />
            <OutputClaim ClaimTypeReferenceId="extension_title" PartnerClaimType="title" />
          </OutputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>

        <!--Demo: Checks if user exists in the migration table. If yes, raises an error -->
        <TechnicalProfile Id="REST-UserMigration-LocalAccount-SignUp">
          <DisplayName>Migrate user sign-in flow</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/raise-error-if-exists</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">Body</Item>
            <Item Key="AllowInsecureAuthInProduction">True</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
          </InputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>

        <!--Demo: Checks if user exists in Azure AD B2C or the migration table. If not, raises an error -->
        <TechnicalProfile Id="REST-UserMigration-LocalAccount-PasswordReset1">
          <DisplayName>Migrate user sign-in flow</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/raise-error-if-not-exists</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">Body</Item>
            <Item Key="AllowInsecureAuthInProduction">True</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
            <InputClaim ClaimTypeReferenceId="objectId" />
          </InputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>

        <TechnicalProfile Id="REST-UserMigration-LocalAccount-PasswordReset2">
          <DisplayName>Migrate user sign-in flow</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/migrate</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">Body</Item>
            <Item Key="AllowInsecureAuthInProduction">True</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
            <!-- <InputClaim ClaimTypeReferenceId="password" /> -->
            <InputClaim ClaimTypeReferenceId="useInputPassword" DefaultValue="true" />
          </InputClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="needToMigrate" />
            <OutputClaim ClaimTypeReferenceId="email" />
            <!-- Don't return the new password <OutputClaim ClaimTypeReferenceId="newPassword" /> -->
            <OutputClaim ClaimTypeReferenceId="displayName" />
            <OutputClaim ClaimTypeReferenceId="givenName" />
            <OutputClaim ClaimTypeReferenceId="surName" />
          </OutputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <!-- Local account Sign-In claims provider -->
    <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/</Item>
            <Item Key="METADATA">https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration</Item>
            <Item Key="authorization_endpoint">https://login.microsoftonline.com/{tenant}/oauth2/token</Item>
            <Item Key="response_types">id_token</Item>
            <Item Key="response_mode">query</Item>
            <Item Key="scope">email openid</Item>
            <Item Key="grant_type">password</Item>

            <!-- Policy Engine Clients -->
            <Item Key="UsePolicyInRedirectUri">false</Item>
            <Item Key="HttpBinding">POST</Item>

            <Item Key="client_id">{Settings:ProxyIdentityExperienceFrameworkAppId}</Item>
            <Item Key="IdTokenAudience">{Settings:IdentityExperienceFramework}</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" />

            <InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{Settings:ProxyIdentityExperienceFrameworkAppId}" />
            <InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="{Settings:IdentityExperienceFramework}" />
          </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" />

            <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
            <OutputClaim ClaimTypeReferenceId="extension_creditorId" /> 
            <OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" />  
            <OutputClaim ClaimTypeReferenceId="extension_likvidoRole" />    
            <OutputClaim ClaimTypeReferenceId="extension_title" />
          </OutputClaims>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <ClaimsProvider>
      <DisplayName>Azure Active Directory</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="AAD-MigrateUserUsingLogonEmail">
          <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,DisableStrongPassword" AlwaysUseDefaultValue="true"/>

            <!-- Optional claims. -->
            <PersistedClaim ClaimTypeReferenceId="givenName" />
            <PersistedClaim ClaimTypeReferenceId="surname" />
            <PersistedClaim ClaimTypeReferenceId="extension_creditorId" />  
            <PersistedClaim ClaimTypeReferenceId="extension_likvidoUserId" />   
            <PersistedClaim ClaimTypeReferenceId="extension_likvidoRole" /> 
            <PersistedClaim ClaimTypeReferenceId="extension_title" />
          </PersistedClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="objectId" />
            <OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
            <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
            <OutputClaim ClaimTypeReferenceId="userPrincipalName" />

            <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
            <OutputClaim ClaimTypeReferenceId="extension_creditorId" /> 
            <OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" />  
            <OutputClaim ClaimTypeReferenceId="extension_likvidoRole" />    
            <OutputClaim ClaimTypeReferenceId="extension_title" />
          </OutputClaims>
          <IncludeTechnicalProfile ReferenceId="AAD-Common" />
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <!-- Facebook claims provider -->
    <ClaimsProvider>
      <DisplayName>Facebook</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="Facebook-OAUTH">
          <Metadata>
            <!--Demo action required: Change to your Facebook App Id-->
            <Item Key="client_id">TODO</Item>
            <Item Key="scope">email public_profile</Item>
            <Item Key="ClaimsEndpoint">https://graph.facebook.com/me?fields=id,first_name,last_name,name,email</Item>
          </Metadata>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>
  </ClaimsProviders>

  <!--<UserJourneys>
  </UserJourneys>-->

</TrustFrameworkPolicy>

Uploading this custom profile works fine, but when I also add these claims to the SignUpOrSignIn.xml file, then I receive validation errors. This is my complete SignUpOrSignIn.xml file:

<?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="{Settings:Tenant}" PolicyId="B2C_1A_JITMigraion_signup_signin" PublicPolicyUri="http://{Settings:Tenant}/B2C_1A_JITMigraion_signup_signin">

  <BasePolicy>
    <TenantId>{Settings:Tenant}</TenantId>
    <PolicyId>B2C_1A_JITMigraion_TrustFrameworkExtensions</PolicyId>
  </BasePolicy>

  <RelyingParty>
    <DefaultUserJourney ReferenceId="SignUpOrSignIn" />

    <TechnicalProfile Id="PolicyProfile">
      <DisplayName>PolicyProfile</DisplayName>
      <Protocol Name="OpenIdConnect" />
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration, DisableStrongPassword"/>
      </InputClaims>
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="displayName" />
        <OutputClaim ClaimTypeReferenceId="givenName" />
        <OutputClaim ClaimTypeReferenceId="surname" />
        <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
        <OutputClaim ClaimTypeReferenceId="identityProvider" />
        <OutputClaim ClaimTypeReferenceId="needToMigrate" DefaultValue="false" />

        <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email" />
        <OutputClaim ClaimTypeReferenceId="extension_creditorId" PartnerClaimType="creditorId" />   
        <OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" PartnerClaimType="likvidoUserId" /> 
        <OutputClaim ClaimTypeReferenceId="extension_likvidoRole" PartnerClaimType="likvidoRole" /> 
        <OutputClaim ClaimTypeReferenceId="extension_title" PartnerClaimType="title" />
      </OutputClaims>
      <SubjectNamingInfo ClaimType="sub" />
    </TechnicalProfile>
  </RelyingParty>
</TrustFrameworkPolicy>

I receive these error messages when I try to upload this policy:

Validation failed: 4 validation error(s) found in policy "B2C_1A_JITMIGRAION_SIGNUP_SIGNIN" of tenant "likvidostaging.onmicrosoft.com".

Claim type "extension_creditorId" is the output claim of the relying party's technical profile, but it is not an output claim in any of the steps of user journey "SignUpOrSignIn".

Claim type "extension_likvidoUserId" is the output claim of the relying party's technical profile, but it is not an output claim in any of the steps of user journey "SignUpOrSignIn".

Claim type "extension_likvidoRole" is the output claim of the relying party's technical profile, but it is not an output claim in any of the steps of user journey "SignUpOrSignIn".

Claim type "extension_title" is the output claim of the relying party's technical profile, but it is not an output claim in any of the steps of user journey "SignUpOrSignIn".

I don't understand what I am doing wrong here, so any help is very much appreciated!

CodePudding user response:

The scope of the output claims of a validation technical profile is limited to the self-asserted technical profile that invokes the validation technical profile, and its validation technical profiles. If you want to use the output claims in the next orchestration step, add the output claims to the self-asserted technical profile that invokes the validation technical profile.

https://docs.microsoft.com/en-us/azure/active-directory-b2c/validation-technical-profile

CodePudding user response:

I figured out how to make it work now. Apparently, it is required to also add these output claims to the SelfAsserted-LocalAccountSignin-Email technical profile (no idea why).

So, my resulting TrustedFrameworkExtensions.xml looks like this:

<?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="{Settings:Tenant}" PolicyId="B2C_1A_JITMigraion_TrustFrameworkExtensions" PublicPolicyUri="http://{Settings:Tenant}/B2C_1A_JITMigraion_TrustFrameworkExtensions">

  <BasePolicy>
    <TenantId>{Settings:Tenant}</TenantId>
    <PolicyId>B2C_1A_JITMigraion_TrustFrameworkBase</PolicyId>
  </BasePolicy>
  <BuildingBlocks>
    <ClaimsSchema>
      <!--Demo: This claim indicates whether the user need to migrate-->
      <ClaimType Id="needToMigrate">
        <DisplayName>needToMigrate</DisplayName>
        <DataType>string</DataType>
        <AdminHelpText>Indicates whether the user need to migrate</AdminHelpText>
        <UserHelpText>Indicates whether the user need to migrate</UserHelpText>
      </ClaimType>
      <ClaimType Id="useInputPassword">
        <DisplayName>useInputPassword</DisplayName>
        <DataType>boolean</DataType>
      </ClaimType>

      <ClaimType Id="extension_creditorId">
        <DisplayName>Creditor ID</DisplayName>
        <DataType>int</DataType>
        <AdminHelpText>The ID of the creditor this user belongs to</AdminHelpText>
        <UserHelpText>The ID of the creditor this user belongs to</UserHelpText>
      </ClaimType>
      <ClaimType Id="extension_likvidoUserId">
        <DisplayName>Likvido user ID</DisplayName>
        <DataType>string</DataType>
        <AdminHelpText>The ID of the user in the Likvido DB</AdminHelpText>
        <UserHelpText>The ID of the user in the Likvido DB</UserHelpText>
      </ClaimType>
      <ClaimType Id="extension_likvidoRole">
        <DisplayName>Likvido user role</DisplayName>
        <DataType>string</DataType>
        <AdminHelpText>The role of the user in Likvido</AdminHelpText>
        <UserHelpText>The role of the user in Likvido</UserHelpText>
      </ClaimType>
      <ClaimType Id="extension_title">
        <DisplayName>Likvido user title</DisplayName>
        <DataType>string</DataType>
        <AdminHelpText>The title of the user</AdminHelpText>
        <UserHelpText>The title of the user</UserHelpText>
      </ClaimType>
    </ClaimsSchema>
  </BuildingBlocks>

  <ClaimsProviders>
    <ClaimsProvider>
      <DisplayName>Azure Active Directory</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="AAD-Common">
          <Metadata>
            <!--Insert b2c-extensions-app application ID here, for example: 11111111-1111-1111-1111-111111111111-->  
            <Item Key="ClientId">{Settings:B2CExtensionsAppClientId}</Item>
            <!--Insert b2c-extensions-app application ObjectId here, for example: 22222222-2222-2222-2222-222222222222-->
            <Item Key="ApplicationObjectId">{Settings:B2CExtensionsAppObjectId}</Item>
          </Metadata>
        </TechnicalProfile>
      </TechnicalProfiles> 
    </ClaimsProvider>
    <!-- Local account Sign-Up claims provider -->
    <ClaimsProvider>
      <DisplayName>Local Account</DisplayName>
      <TechnicalProfiles>

        <!-- SIGN-IN -->
        <TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="needToMigrate" />
            <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
            <OutputClaim ClaimTypeReferenceId="extension_creditorId" /> 
            <OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" />  
            <OutputClaim ClaimTypeReferenceId="extension_likvidoRole" />    
            <OutputClaim ClaimTypeReferenceId="extension_title" />
          </OutputClaims>
          <ValidationTechnicalProfiles>
            <!--Demo: Add user migration validation technical profile before login-NonInteractive -->
            <ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-SignIn" ContinueOnError="false" />

            <!--Demo: Run this validation technical profile only if user doesn't need to migrate -->
            <ValidationTechnicalProfile ReferenceId="login-NonInteractive">
              <Preconditions>
                <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
                  <Value>needToMigrate</Value>
                  <Value>local</Value>
                  <Action>SkipThisValidationTechnicalProfile</Action>
                </Precondition>
              </Preconditions>
            </ValidationTechnicalProfile>

            <!--Demo: Run this validation technical profile only if user needs to migrate -->
            <ValidationTechnicalProfile ReferenceId="AAD-MigrateUserUsingLogonEmail">
              <Preconditions>
                <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
                  <Value>needToMigrate</Value>
                  <Value>local</Value>
                  <Action>SkipThisValidationTechnicalProfile</Action>
                </Precondition>
              </Preconditions>
            </ValidationTechnicalProfile>

          </ValidationTechnicalProfiles>
        </TechnicalProfile>

        <!-- SIGN-UP -->
        <TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
          <Metadata>
            <Item Key="EnforceEmailVerification">False</Item>
          </Metadata>
          <ValidationTechnicalProfiles>
            <!--Demo: Add user migration validation technical profile before AAD-UserWriteUsingLogonEmail -->
            <ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-SignUp" ContinueOnError="false" />
            <ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" ContinueOnError="false" />
          </ValidationTechnicalProfiles>
        </TechnicalProfile>

        <!-- PASSWORD RESET first page -->
        <TechnicalProfile Id="LocalAccountDiscoveryUsingEmailAddress">
          <Metadata>
            <Item Key="EnforceEmailVerification">False</Item>
          </Metadata>
          <ValidationTechnicalProfiles>
            <ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" ContinueOnError="true" />
            <ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-PasswordReset1" ContinueOnError="false" />
          </ValidationTechnicalProfiles>
        </TechnicalProfile>

        <!-- PASSWORD RESET second page -->
        <TechnicalProfile Id="LocalAccountWritePasswordUsingObjectId">
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="objectId" />
            <OutputClaim ClaimTypeReferenceId="authenticationSource" />
          </OutputClaims>
          <ValidationTechnicalProfiles>
            <ValidationTechnicalProfile ReferenceId="AAD-UserWritePasswordUsingObjectId">
              <!--Don't run this validation technical profile if objectId is not exists (migrated acccount)-->
              <Preconditions>
                <Precondition Type="ClaimsExist" ExecuteActionsIf="false">
                  <Value>objectId</Value>
                  <Action>SkipThisValidationTechnicalProfile</Action>
                </Precondition>
              </Preconditions>
            </ValidationTechnicalProfile>

            <ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-PasswordReset2">
              <!--Don't run this validation technical profile if objectId is exists (existing acccount)-->
              <Preconditions>
                <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
                  <Value>objectId</Value>
                  <Action>SkipThisValidationTechnicalProfile</Action>
                </Precondition>
              </Preconditions>
            </ValidationTechnicalProfile>

            <ValidationTechnicalProfile ReferenceId="AAD-MigrateUserUsingLogonEmail">
              <!--Don't run this validation technical profile if objectId is exists (existing acccount)-->
              <Preconditions>
                <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
                  <Value>objectId</Value>
                  <Action>SkipThisValidationTechnicalProfile</Action>
                </Precondition>
              </Preconditions>
            </ValidationTechnicalProfile>

          </ValidationTechnicalProfiles>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <ClaimsProvider>
      <DisplayName>REST APIs</DisplayName>
      <TechnicalProfiles>

        <!--Demo: Checks if user exists in the migration table. If yes, validate the credentials and migrate the account -->
        <TechnicalProfile Id="REST-UserMigration-LocalAccount-SignIn">
          <DisplayName>Migrate user sign-in flow</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/migrate</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">Body</Item>
            <Item Key="AllowInsecureAuthInProduction">True</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="signInName" />
            <InputClaim ClaimTypeReferenceId="password" />
            <InputClaim ClaimTypeReferenceId="useInputPassword" DefaultValue="false" />
          </InputClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="needToMigrate" />
            <OutputClaim ClaimTypeReferenceId="email" />
            <OutputClaim ClaimTypeReferenceId="newPassword" />
            <OutputClaim ClaimTypeReferenceId="displayName" />
            <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="firstName" />
            <OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="lastName" />
            <OutputClaim ClaimTypeReferenceId="extension_creditorId" PartnerClaimType="creditorId" />
            <OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" PartnerClaimType="likvidoUserId" />
            <OutputClaim ClaimTypeReferenceId="extension_likvidoRole" PartnerClaimType="likvidoRole" />
            <OutputClaim ClaimTypeReferenceId="extension_title" PartnerClaimType="title" />
          </OutputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>

        <!--Demo: Checks if user exists in the migration table. If yes, raises an error -->
        <TechnicalProfile Id="REST-UserMigration-LocalAccount-SignUp">
          <DisplayName>Migrate user sign-in flow</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/raise-error-if-exists</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">Body</Item>
            <Item Key="AllowInsecureAuthInProduction">True</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
          </InputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>

        <!--Demo: Checks if user exists in Azure AD B2C or the migration table. If not, raises an error -->
        <TechnicalProfile Id="REST-UserMigration-LocalAccount-PasswordReset1">
          <DisplayName>Migrate user sign-in flow</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/raise-error-if-not-exists</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">Body</Item>
            <Item Key="AllowInsecureAuthInProduction">True</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
            <InputClaim ClaimTypeReferenceId="objectId" />
          </InputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>

        <TechnicalProfile Id="REST-UserMigration-LocalAccount-PasswordReset2">
          <DisplayName>Migrate user sign-in flow</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/migrate</Item>
            <Item Key="AuthenticationType">None</Item>
            <Item Key="SendClaimsIn">Body</Item>
            <Item Key="AllowInsecureAuthInProduction">True</Item>
          </Metadata>
          <InputClaims>
            <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
            <!-- <InputClaim ClaimTypeReferenceId="password" /> -->
            <InputClaim ClaimTypeReferenceId="useInputPassword" DefaultValue="true" />
          </InputClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="needToMigrate" />
            <OutputClaim ClaimTypeReferenceId="email" />
            <!-- Don't return the new password <OutputClaim ClaimTypeReferenceId="newPassword" /> -->
            <OutputClaim ClaimTypeReferenceId="displayName" />
            <OutputClaim ClaimTypeReferenceId="givenName" />
            <OutputClaim ClaimTypeReferenceId="surName" />
          </OutputClaims>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <!-- Local account Sign-In claims provider -->
    <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/</Item>
            <Item Key="METADATA">https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration</Item>
            <Item Key="authorization_endpoint">https://login.microsoftonline.com/{tenant}/oauth2/token</Item>
            <Item Key="response_types">id_token</Item>
            <Item Key="response_mode">query</Item>
            <Item Key="scope">email openid</Item>
            <Item Key="grant_type">password</Item>

            <!-- Policy Engine Clients -->
            <Item Key="UsePolicyInRedirectUri">false</Item>
            <Item Key="HttpBinding">POST</Item>

            <Item Key="client_id">{Settings:ProxyIdentityExperienceFrameworkAppId}</Item>
            <Item Key="IdTokenAudience">{Settings:IdentityExperienceFramework}</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" />

            <InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{Settings:ProxyIdentityExperienceFrameworkAppId}" />
            <InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="{Settings:IdentityExperienceFramework}" />
          </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" />

            <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
            <OutputClaim ClaimTypeReferenceId="extension_creditorId" /> 
            <OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" />  
            <OutputClaim ClaimTypeReferenceId="extension_likvidoRole" />    
            <OutputClaim ClaimTypeReferenceId="extension_title" />
          </OutputClaims>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <ClaimsProvider>
      <DisplayName>Azure Active Directory</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="AAD-MigrateUserUsingLogonEmail">
          <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,DisableStrongPassword" AlwaysUseDefaultValue="true"/>

            <!-- Optional claims. -->
            <PersistedClaim ClaimTypeReferenceId="givenName" />
            <PersistedClaim ClaimTypeReferenceId="surname" />
            <PersistedClaim ClaimTypeReferenceId="extension_creditorId" />  
            <PersistedClaim ClaimTypeReferenceId="extension_likvidoUserId" />   
            <PersistedClaim ClaimTypeReferenceId="extension_likvidoRole" /> 
            <PersistedClaim ClaimTypeReferenceId="extension_title" />
          </PersistedClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="objectId" />
            <OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
            <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
            <OutputClaim ClaimTypeReferenceId="userPrincipalName" />

            <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
            <OutputClaim ClaimTypeReferenceId="extension_creditorId" /> 
            <OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" />  
            <OutputClaim ClaimTypeReferenceId="extension_likvidoRole" />    
            <OutputClaim ClaimTypeReferenceId="extension_title" />
          </OutputClaims>
          <IncludeTechnicalProfile ReferenceId="AAD-Common" />
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <!-- Facebook claims provider -->
    <ClaimsProvider>
      <DisplayName>Facebook</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="Facebook-OAUTH">
          <Metadata>
            <!--Demo action required: Change to your Facebook App Id-->
            <Item Key="client_id">TODO</Item>
            <Item Key="scope">email public_profile</Item>
            <Item Key="ClaimsEndpoint">https://graph.facebook.com/me?fields=id,first_name,last_name,name,email</Item>
          </Metadata>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>
  </ClaimsProviders>

  <!--<UserJourneys>
  </UserJourneys>-->

</TrustFrameworkPolicy>
  • Related