We have built a service that calls cosmos DB for custom claims to be sent in ID token. The output of the REST API is as per the expected token format below
{"personalAttributes":{"guid":"1b92e96e28b14737acac11d23dcdd3d0","familyName":"ABC","givenName":"PQR","name":"PQR","preferredUserName":"PQR","upn":"PQR#XYZ.com",
"physicalLocation":"London, UK"
"roles":"Engineer","Engineer","Engineer",
"scopes":{"scopeName":"Application 2 - Pipe Spool install","initiative":"nan","operation":"nan","WBS":"nan"},
{"roleName":"Engineer","jobPosition":"Engineer","rolePermissions":"read, report, archive"},
{"roleName":"Engineer","jobPosition":"Engineer","rolePermissions":"read, report, archive"},
{"roleName":"Engineer","jobPosition":"Engineer","rolePermissions":"read, report, archive"}}
However, when B2C includes this in the ID Token it is appending / and . We tried formatting the output which is resulting in B2C not even generating the ID token. PLease advise how we can overcome this
TIA
It looks like you are trying to ask B2C to output a complex object as a claim value. This isn't supported so B2C casting the object to a string - resulting the escaped JSON data you are seeing.
Edit for additional clarity: The personalAttributes value returned from CosmosDB is not a StringCollection (array) as you have defined in the ClaimsSchema. It is a JSON object. B2C does not support object claim types - you'll need to extract the exact values you want from the object.
<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="xxxx.onmicrosoft.com" PolicyId="B2C_1A_oboTrustFrameworkExtensions" PublicPolicyUri="http://xxxx.onmicrosoft.com/B2C_1A_oboTrustFrameworkExtensions" TenantObjectId="3dc9a24f-ed39-4635-b182-e35aadde9cb2">
<BasePolicy>
<TenantId>xxxx.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_oboTrustFrameworkBase</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="clientId">
<DisplayName>OIDC client id</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="personalAttributes">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="roles">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="scopes">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="tokenOwner">
<DisplayName>aud claim in OBO token</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="jsonSourceClaim">
<DisplayName>JSON output from Custom Service</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="GetClaimsFromJson">
<DisplayName>JSON output from Custom Service</DisplayName>
<DataType>string</DataType>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<ClaimsTransformation Id="AssertClientIdAndAudAreEqual" TransformationMethod="AssertStringClaimsAreEqual">
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" TransformationClaimType="inputClaim1" />
<InputClaim ClaimTypeReferenceId="tokenOwner" TransformationClaimType="inputClaim2" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringComparison" DataType="string" Value="ordinalIgnoreCase" />
</InputParameters>
</ClaimsTransformation>
<ClaimsTransformation Id="GetClaimsFromJsonTransformation" TransformationMethod="GetClaimsFromJsonArray">
<InputClaims>
<InputClaim ClaimTypeReferenceId="jsonSourceClaim" TransformationClaimType="jsonSource" />
</InputClaims>
<InputParameters>
<InputParameter Id="errorOnMissingClaims" DataType="boolean" Value="false" />
<InputParameter Id="includeEmptyClaims" DataType="boolean" Value="false" />
<InputParameter Id="jsonSourceKeyName" DataType="string" Value="key" />
<InputParameter Id="jsonSourceValueName" DataType="string" Value="value" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="personalAttributes" />
<OutputClaim ClaimTypeReferenceId="roles" />
<OutputClaim ClaimTypeReferenceId="scopes" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
<ClaimsProviders>
<!-- Used for custom extension attribute storage -->
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<Item Key="ApplicationObjectId">xxxxxx</Item>
<Item Key="ClientId">xxxxxx</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<Domain>bechtel</Domain>
<DisplayName>Bechtel AAD</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Bechtel-AAD">
<DisplayName>Work or school account</DisplayName>
<Description>Login with your work or school account</Description>
<Protocol Name="OpenIdConnect" />
<Metadata>
<Item Key="METADATA">https://login.microsoftonline.com/xxxx/v2.0/.well-known/openid-configuration</Item>
<!-- Update the Client ID below to the Application ID -->
<Item Key="client_id">xxxxxx</Item>
<Item Key="response_types">code</Item>
<Item Key="scope">openid profile</Item>
<Item Key="response_mode">form_post</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="DiscoverMetadataByTokenIssuer">true</Item>
<!-- The key below allows you to specify each of the Azure AD tenants that can be used to sign in. Update the GUIDs below for each tenant. -->
<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/22d635a3-3930-4779-a82d-155e2d13b75e</Item>
<!-- The commented key below specifies that users from any tenant can sign-in. Uncomment if you would like anyone with an Azure AD account to be able to sign in. -->
<!--<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/</Item> -->
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_BECHTELSECRET" />
</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" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="preferred_username" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
<!--OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" /-->
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="bechtel" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Facebook</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Facebook-OAUTH">
<Metadata>
<Item Key="client_id">facebook_clientid</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>
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">db6c8fe9-368f-48a0-af63-19ad47d91e90</Item>
<Item Key="IdTokenAudience">5169b8e9-5bcd-4b04-b5f1-d3ca801bbbef</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="db6c8fe9-368f-48a0-af63-19ad47d91e90" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="5169b8e9-5bcd-4b04-b5f1-d3ca801bbbef" />
</InputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-GetCustomClaim">
<DisplayName>Get a custom claim</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://beciamobo.azurewebsites.net/CustomClaims</Item> -->
<Item Key="ServiceUrl">https://xxxxx.azurewebsites.net/xxxxx</Item>
<Item Key="SendClaimsIn">QueryString</Item>
<!-- Set AuthenticationType to Basic or ClientCertificate in production environments -->
<Item Key="AuthenticationType">None</Item>
<!-- REMOVE the following line in production environments -->
<Item Key="AllowInsecureAuthInProduction">true</Item>
</Metadata>
<InputClaims>
<!-- Claims sent to your REST API -->
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="{OIDC:Scope}" AlwaysUseDefaultValue="true" />
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{OIDC:ClientId}" AlwaysUseDefaultValue="true" />
</InputClaims>
<OutputClaims>
<!-- Claims parsed from your REST API -->
<OutputClaim ClaimTypeReferenceId="personalAttributes" />
<OutputClaim ClaimTypeReferenceId="roles" />
<OutputClaim ClaimTypeReferenceId="scopes" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetClaimsFromJsonTransformation" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Self Asserted</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="ValidateOBORequest">
<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="tokenOwner" />
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{OIDC:ClientId}" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="tokenOwner" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="AssertClientIdAndAudAreEqual" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SUSI">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="AADExchange" />
<!--ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" /-->
</ClaimsProviderSelections>
<!--ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges-->
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<!--Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions-->
<ClaimsExchanges>
<ClaimsExchange Id="AADExchange" TechnicalProfileReferenceId="Bechtel-AAD" />
<!--ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" /-->
</ClaimsExchanges>
</OrchestrationStep>
<!-- For social IDP authentication, attempt to find the user account in the directory. -->
<OrchestrationStep Order="3" 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>
<!-- 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>
<!-- The previous step (SelfAsserted-Social) could have been skipped if there were no attributes to collect
from the user. So, in that case, create the user in the directory if one does not already exist
(verified using objectId which would be set from the last step if account was created in the directory. -->
<OrchestrationStep Order="5" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTGetCustomClaim" TechnicalProfileReferenceId="REST-GetCustomClaim" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="7" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
</UserJourney>
<UserJourney Id="OBO">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="Validate" TechnicalProfileReferenceId="ValidateOBORequest" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTGetCustomClaim" TechnicalProfileReferenceId="REST-GetCustomClaim" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
</TrustFrameworkPolicy>
Please see policy below
<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="xxxx.onmicrosoft.com" PolicyId="B2C_1A_oboTrustFrameworkExtensions" PublicPolicyUri="http://xxxx.onmicrosoft.com/B2C_1A_oboTrustFrameworkExtensions" TenantObjectId="3dc9a24f-ed39-4635-b182-e35aadde9cb2">
<BasePolicy>
<TenantId>xxxx.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_oboTrustFrameworkBase</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="clientId">
<DisplayName>OIDC client id</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="personalAttributes">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="roles">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="scopes">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="tokenOwner">
<DisplayName>aud claim in OBO token</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="jsonSourceClaim">
<DisplayName>JSON output from Custom Service</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="GetClaimsFromJson">
<DisplayName>JSON output from Custom Service</DisplayName>
<DataType>string</DataType>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<ClaimsTransformation Id="AssertClientIdAndAudAreEqual" TransformationMethod="AssertStringClaimsAreEqual">
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" TransformationClaimType="inputClaim1" />
<InputClaim ClaimTypeReferenceId="tokenOwner" TransformationClaimType="inputClaim2" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringComparison" DataType="string" Value="ordinalIgnoreCase" />
</InputParameters>
</ClaimsTransformation>
<ClaimsTransformation Id="GetClaimsFromJsonTransformation" TransformationMethod="GetClaimsFromJsonArray">
<InputClaims>
<InputClaim ClaimTypeReferenceId="jsonSourceClaim" TransformationClaimType="jsonSource" />
</InputClaims>
<InputParameters>
<InputParameter Id="errorOnMissingClaims" DataType="boolean" Value="false" />
<InputParameter Id="includeEmptyClaims" DataType="boolean" Value="false" />
<InputParameter Id="jsonSourceKeyName" DataType="string" Value="key" />
<InputParameter Id="jsonSourceValueName" DataType="string" Value="value" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="personalAttributes" />
<OutputClaim ClaimTypeReferenceId="roles" />
<OutputClaim ClaimTypeReferenceId="scopes" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
<ClaimsProviders>
<!-- Used for custom extension attribute storage -->
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<Item Key="ApplicationObjectId">xxxxxx</Item>
<Item Key="ClientId">xxxxxx</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<Domain>bechtel</Domain>
<DisplayName>Bechtel AAD</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Bechtel-AAD">
<DisplayName>Work or school account</DisplayName>
<Description>Login with your work or school account</Description>
<Protocol Name="OpenIdConnect" />
<Metadata>
<Item Key="METADATA">https://login.microsoftonline.com/xxxx/v2.0/.well-known/openid-configuration</Item>
<!-- Update the Client ID below to the Application ID -->
<Item Key="client_id">xxxxxx</Item>
<Item Key="response_types">code</Item>
<Item Key="scope">openid profile</Item>
<Item Key="response_mode">form_post</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="DiscoverMetadataByTokenIssuer">true</Item>
<!-- The key below allows you to specify each of the Azure AD tenants that can be used to sign in. Update the GUIDs below for each tenant. -->
<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/22d635a3-3930-4779-a82d-155e2d13b75e</Item>
<!-- The commented key below specifies that users from any tenant can sign-in. Uncomment if you would like anyone with an Azure AD account to be able to sign in. -->
<!--<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/</Item> -->
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_BECHTELSECRET" />
</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" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="preferred_username" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
<!--OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" /-->
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="bechtel" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Facebook</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Facebook-OAUTH">
<Metadata>
<Item Key="client_id">facebook_clientid</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>
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">db6c8fe9-368f-48a0-af63-19ad47d91e90</Item>
<Item Key="IdTokenAudience">5169b8e9-5bcd-4b04-b5f1-d3ca801bbbef</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="db6c8fe9-368f-48a0-af63-19ad47d91e90" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="5169b8e9-5bcd-4b04-b5f1-d3ca801bbbef" />
</InputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-GetCustomClaim">
<DisplayName>Get a custom claim</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://beciamobo.azurewebsites.net/CustomClaims</Item> -->
<Item Key="ServiceUrl">https://xxxxx.azurewebsites.net/xxxxx</Item>
<Item Key="SendClaimsIn">QueryString</Item>
<!-- Set AuthenticationType to Basic or ClientCertificate in production environments -->
<Item Key="AuthenticationType">None</Item>
<!-- REMOVE the following line in production environments -->
<Item Key="AllowInsecureAuthInProduction">true</Item>
</Metadata>
<InputClaims>
<!-- Claims sent to your REST API -->
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="{OIDC:Scope}" AlwaysUseDefaultValue="true" />
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{OIDC:ClientId}" AlwaysUseDefaultValue="true" />
</InputClaims>
<OutputClaims>
<!-- Claims parsed from your REST API -->
<OutputClaim ClaimTypeReferenceId="personalAttributes" />
<OutputClaim ClaimTypeReferenceId="roles" />
<OutputClaim ClaimTypeReferenceId="scopes" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetClaimsFromJsonTransformation" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Self Asserted</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="ValidateOBORequest">
<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="tokenOwner" />
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{OIDC:ClientId}" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="tokenOwner" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="AssertClientIdAndAudAreEqual" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SUSI">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="AADExchange" />
<!--ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" /-->
</ClaimsProviderSelections>
<!--ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges-->
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<!--Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions-->
<ClaimsExchanges>
<ClaimsExchange Id="AADExchange" TechnicalProfileReferenceId="Bechtel-AAD" />
<!--ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" /-->
</ClaimsExchanges>
</OrchestrationStep>
<!-- For social IDP authentication, attempt to find the user account in the directory. -->
<OrchestrationStep Order="3" 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>
<!-- 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>
<!-- The previous step (SelfAsserted-Social) could have been skipped if there were no attributes to collect
from the user. So, in that case, create the user in the directory if one does not already exist
(verified using objectId which would be set from the last step if account was created in the directory. -->
<OrchestrationStep Order="5" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTGetCustomClaim" TechnicalProfileReferenceId="REST-GetCustomClaim" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="7" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
</UserJourney>
<UserJourney Id="OBO">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="Validate" TechnicalProfileReferenceId="ValidateOBORequest" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTGetCustomClaim" TechnicalProfileReferenceId="REST-GetCustomClaim" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
</TrustFrameworkPolicy>
You need to apply a GetClaimFromJson transformation to extract claims from JSON.
We have custom policies for Azure B2C with MFA enabled.
After successful login, when trying to acquire access token using acquireTokenSilent(), getting the below errors
Refused to display 'https://accounts.google.com/signin/oauth?client_id=xxx&redirect_uri=xxxxx/oauth2/authresp&response_type=code&scope=email+profile&login_hintXXXXXXXXXXX' in a frame because it set 'X-Frame-Options' to 'deny'.
Followed by ClientAuthError: Token renewal operation failed due to timeout.
or
AADB2C90077: User does not have an existing session and request prompt parameter has a value of 'None'
I am getting these errors after every login. If I try to call acquireTokenPopup(), the popup is showing the MFA flow again and asking to authenticate the phone number which is not desired.
I have read many posts regarding these but unable to fix the issue.
Google technical profile:
<TechnicalProfile Id="Google-OAUTH">
<DisplayName>Google</DisplayName>
<Protocol Name="OAuth2" />
<Metadata>
<Item Key="ProviderName">google</Item>
<Item Key="authorization_endpoint">https://accounts.google.com/o/oauth2/auth</Item>
<Item Key="AccessTokenEndpoint">https://accounts.google.com/o/oauth2/token</Item>
<Item Key="ClaimsEndpoint">https://www.googleapis.com/oauth2/v1/userinfo</Item>
<Item Key="scope">email profile</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="UsePolicyInRedirectUri">0</Item>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
<Item Key="client_id">XXXXXXXXXXXXXXXX</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_GoogleSecret" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="loginHint" PartnerClaimType="login_hint" DefaultValue="{OIDC:LoginHint}" />
<InputClaim ClaimTypeReferenceId="prompt" PartnerClaimType="prompt" DefaultValue="{OAUTH-KV:account_prompt}" AlwaysUseDefaultValue="true"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="id" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="google.com" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" />
<OutputClaim ClaimTypeReferenceId="requiresMFA" DefaultValue="true"/>
<OutputClaim ClaimTypeReferenceId="isAccessFlow" DefaultValue="{OAUTH-KV:access_flow}" AlwaysUseDefaultValue="true"/>
<OutputClaim ClaimTypeReferenceId="prompt" DefaultValue="{OAUTH-KV:account_prompt}" AlwaysUseDefaultValue="true"/>
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>
Phone Factor Technical profile:
<TechnicalProfile Id="PhoneFactor-InputOrVerify">
<DisplayName>PhoneFactor</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.PhoneFactorProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.phonefactor1.1</Item>
<Item Key="ManualPhoneNumberEntryAllowed">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUserIdForMFA" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="UserId" />
<InputClaim ClaimTypeReferenceId="strongAuthenticationPhoneNumber" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="Verified.OfficePhone" />
<OutputClaim ClaimTypeReferenceId="newPhoneNumberEntered" PartnerClaimType="newPhoneNumberEntered" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-MFA" />
</TechnicalProfile>
MFA related orchestration steps:
<OrchestrationStep Order="7" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>isActiveMFASession</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>requiresMFA</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>isAccessFlow</Value>
<Value>true</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="PhoneFactor-Verify" TechnicalProfileReferenceId="PhoneFactor-InputOrVerify" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="8" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>newPhoneNumberEntered</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWriteWithObjectId" TechnicalProfileReferenceId="AAD-UserWritePhoneNumberUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
requiresMFA and isAccesssFow are two custom attributes i am using.
requiresMFA to stop MFA for few providers
isAccessFlow is to avoid MFA in acquire token popup.I am passing this in request object from frontend while calling acquireTokenPopup when silent calls are failing with any of the above errors.
Please let me who how i can avoid getting those errors. These attributes i used as a temporary workaround.
I have some b2c SAML CustomPolicy for one SP.
Here I have some OutputClaims. Now I want to send one Claim with some logic and tried to set OutputClaimTransformations.
My scenario:
I will send the claim "ABCString: 0,1" for Croatia and "ABCString: 0,2" for germany. 0 is some default value I need to send and the next value should be country dependent.
So I created one Transformation for defaultvalue, then one for country dependent, then I merge both in one collection and will join them in one string.
I attached my XML.
Problem is: the "ABCString" is never sent to application. The OutputClaims of the ClaimsTransformations are not used at all.
All examples I found were in Extension or Base File and are using "persistent" fields. I just want to build the value on request.
Some ideas for this?
my 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="xxx.onmicrosoft.com"
PolicyId="B2C_1A_signup_signin_xxx-test"
PublicPolicyUri="http://xxx.onmicrosoft.com/B2C_1A_signup_signin_xxx-test" >
<BasePolicy>
<TenantId>xxx.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="ABCCollection">
<DisplayName>abc</DisplayName>
<DataType>stringCollection</DataType>
<UserHelpText>abc.</UserHelpText>
</ClaimType>
<ClaimType Id="ABCString">
<DisplayName>abcs</DisplayName>
<DataType>string</DataType>
<UserHelpText>abcs.</UserHelpText>
</ClaimType>
<ClaimType Id="DEFString">
<DisplayName>defs</DisplayName>
<DataType>string</DataType>
<UserHelpText>defs</UserHelpText>
</ClaimType>
<ClaimType Id="GHIString">
<DisplayName>ghis</DisplayName>
<DataType>string</DataType>
<UserHelpText>ghis.</UserHelpText>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<ClaimsTransformation Id="SetDefault" TransformationMethod="AddParameterToStringCollection">
<InputClaims>
<InputClaim ClaimTypeReferenceId="ABCString" TransformationClaimType="collection" />
</InputClaims>
<InputParameters>
<InputParameter Id="item" DataType="string" Value="0" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ABCCollection" TransformationClaimType="collection" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="GetCountrySpecific" TransformationMethod="LookupValue">
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_country" TransformationClaimType="inputParameterId" />
</InputClaims>
<InputParameters>
<InputParameter Id="CROATIA" DataType="string" Value="1" />
<InputParameter Id="GERMANY" DataType="string" Value="2" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="DEFString" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="MergeDefaultWithCountry" TransformationMethod="AddItemToStringCollection">
<InputClaims>
<InputClaim ClaimTypeReferenceId="DEFString" TransformationClaimType="item" />
<InputClaim ClaimTypeReferenceId="ABCCollection" TransformationClaimType="collection" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ABCCollection" TransformationClaimType="collection" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="ConvertTeamCollectionToString" TransformationMethod="StringJoin">
<InputClaims>
<InputClaim ClaimTypeReferenceId="ABCCollection" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter DataType="string" Id="delimiter" Value="," />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ABCString" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
<ContentDefinitions>
<ContentDefinition Id="api.signuporsignin">
<LoadUri>https://api.contoso.com/test/azure/html/xxx.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:unifiedssp:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Signin and Signup</Item>
</Metadata>
</ContentDefinition>
</ContentDefinitions>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<Domain>app.contoso.com</Domain>
<DisplayName>SAML2 for App</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Saml2App">
<DisplayName>SAML2 for App</DisplayName>
<Protocol Name="None" />
<OutputTokenFormat>SAML2</OutputTokenFormat>
<Metadata>
<Item Key="IssuerUri">https://xxx.b2clogin.com/xxx.onmicrosoft.com/B2C_1A_signup_signin_xxx-test</Item>
</Metadata>
<CryptographicKeys>
<Key Id="MetadataSigning" StorageReferenceId="B2C_1A_mycert"/>
<Key Id="SamlAssertionSigning" StorageReferenceId="B2C_1A_mycert"/>
<Key Id="SamlMessageSigning" StorageReferenceId="B2C_1A_mycert"/>
</CryptographicKeys>
<InputClaims/>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Saml"/>
</TechnicalProfile>
<TechnicalProfile Id="SM-Saml">
<DisplayName>Session Management Provider</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.SamlSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignUpOrSignInSAML2App">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<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" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="Saml2App" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignInSAML2App" />
<UserJourneyBehaviors>
<ScriptExecution>Allow</ScriptExecution>
</UserJourneyBehaviors>
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="SAML2"/>
<Metadata>
<Item Key="PartnerEntity"><![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><some definition>]]></Item>
<Item Key="client_id">xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</Item>
<Item Key="IdTokenAudience">yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy</Item>
<Item Key="XmlSignatureAlgorithm">Sha256</Item>
</Metadata>
<CryptographicKeys>
<Key Id="MetadataSigning" StorageReferenceId="B2C_1A_mycert"/>
<Key Id="SamlAssertionSigning" StorageReferenceId="B2C_1A_mycert"/>
<Key Id="SamlMessageSigning" StorageReferenceId="B2C_1A_mycert"/>
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="GHIString" DefaultValue="123" />
<OutputClaim ClaimTypeReferenceId="extension_country" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetCountrySpecific" />
<OutputClaimsTransformation ReferenceId="SetDefault" />
<OutputClaimsTransformation ReferenceId="MergeDefaultWithCountry" />
<OutputClaimsTransformation ReferenceId="ConvertTeamCollectionToString" />
</OutputClaimsTransformations>
<SubjectNamingInfo ClaimType="Email" Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" ExcludeAsClaim="false"/>
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
I don't believe any output claims transformations are invoked for a relying party technical profile.
Rather, you must invoke the output claims transformations during the user journey, such as follows.
Define a new claims transformation technical profile that invokes the new output claims transformation:
<TechnicalProfile Id="ClaimsTransformation-CreateABCStringClaim">
<DisplayName>Create ABCString Claim</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ABCString" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetCountrySpecific" />
<OutputClaimsTransformation ReferenceId="SetDefault" />
<OutputClaimsTransformation ReferenceId="MergeDefaultWithCountry" />
<OutputClaimsTransformation ReferenceId="ConvertTeamCollectionToString" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
Add a new orchestration step, before the SendClaims orchestration step, that invokes the claims transformation technical profile:
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="ClaimsTransformation-CreateABCStringClaim" TechnicalProfileReferenceId="ClaimsTransformation-CreateABCStringClaim" />
</ClaimsExchanges>
</OrchestrationStep>
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 :
the SocialAndLocalAccounts version of the starter pack : https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack/tree/master/SocialAndLocalAccounts
the "sign-up with email invitation" example provided by the community : https://github.com/azure-ad-b2c/samples/tree/master/policies/invite
Microsoft's documentation to set-up Google custom policies : https://learn.microsoft.com/fr-fr/azure/active-directory-b2c/active-directory-b2c-setup-goog-app
some custom attributes exemples : https://medium.com/the-new-control-plane/working-with-custom-attributes-in-azure-ad-b2c-custom-policies-fae1454b12bf et https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-create-custom-attributes-profile-edit-custom#next-steps
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?
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 !!
What do I have to change in the custom policy starter pack to make my sign-in/sing-up's policy username based not email based?
Add this technical profile to TrustFrameworkExtensions.xml to write the user to Azure AD
<TechnicalProfile Id="AAD-UserWriteUsingLogonName">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.userName" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.userName" />
<PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="strongAuthenticationEmailAddress" />
<PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password" />
<PersistedClaim ClaimTypeReferenceId="displayName" DefaultValue="SomeDefaultDisplayNameValue" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
Add this technical profile to TrustFrameworkExtensions.xml that is called by the user journey. The critical change here is the <Item Key="LocalAccountType">Username</Item> and <Item Key="LocalAccountProfile">true</Item>
<TechnicalProfile Id="LocalAccountSignUpWithLogonName">
<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="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item>
<Item Key="LocalAccountType">Username</Item>
<Item Key="LocalAccountProfile">true</Item>
<Item Key="language.button_continue">Create</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" Required="true" />
<OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="email" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="newUser" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonName" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
Sigin Technical Profile for Reference
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Username">
<DisplayName>Local Account Signin</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="SignUpTarget">SignUpWithLogonUsernameExchange</Item>
<Item Key="setting.operatingMode">Username</Item>
<Item Key="ContentDefinitionReferenceId">api.selfasserted</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="password" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
Add this user journey to TrustFrameworkExtensions.xml
<UserJourney Id="SignUpOrSignIn">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninUsernameExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninUsernameExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Username" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SignUpWithLogonUsernameExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonName" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- This step reads any user attributes that we may not have received when in the token. -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
thanks to Omer for his help!