Azure AD B2C: Unable to validate - Object reference not set to an instance of an object.
I want to have a CombinedSignInAndSignUp flow in my policy such that the 1st step has option to enter email and a continue button or select the sign-in with passkey option which goes to the FIDOExchange flow.
If the user enters email and it doesn't exist in the ADB2C we trigger an email for verification in subsequent steps. For keeping the email in the same language as the form, a claims transformation of type "GetLocalizedStringsTransformation" is used which worked fine until implementing the above changes.
In the application insights logs I see exception after all claims transformation have run as expected:
{
"Key": "Exception",
"Value": {
"Kind": "Handled",
"HResult": "80131500",
"Message": "Execution of ClaimsTransformationImpl of Type \"Microsoft.Cpim.Data.Transformations.GetLocalizedStringsTransformation\" for TransformationMethod \"GetLocalizedStringsTransformation\" of ClaimsTransformation with id \"GetLocalizedStringsForEmail\" in policy \"B2C_1A_Common\" of tenant \"xxx.onmicrosoft.com\" threw an exception with the following message: Object reference not set to an instance of an object.",
"Data": {
"IsPolicySpecificError": false,
"TenantId": "xxx.onmicrosoft.com",
"PolicyId": "B2C_1A_Common",
"LeafTenantId": "xxx.onmicrosoft.com",
"LeafPolicyId": "B2C_1A_Signin",
"TechnicalProfileId": "OTP-SendEmail",
"ClaimsTransformation.Id": "GetLocalizedStringsForEmail",
"ClaimsTransformation.TransformMethod": "GetLocalizedStringsTransformation",
"ClaimsTransformation.InputClaims.Count": "0",
"ClaimsTransformation.InputParameters.Count": "0",
"ClaimsTransformation.OutputClaims.Count": "8"
},
"Exception": {
"Kind": "Handled",
"HResult": "80004003",
"Message": "Object reference not set to an instance of an object.",
"Data": {}
}
}
}
User journey:
<UserJourneys>
<UserJourney Id="SignUpOrSignIn">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="EmailExchange"/>
<ClaimsProviderSelection TargetClaimsExchangeId="FidoExchange"/>
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="EmailExchange" TechnicalProfileReferenceId="FORM-Email"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>signInName</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="FidoExchange" TechnicalProfileReferenceId="REST-FIDOGetChallengeForSignIn"/>
</ClaimsExchanges>
</OrchestrationStep>
Content definitions:
<ContentDefinition Id="api.selfasserted">
<LoadUri>/unified?ui_locales={Culture:LanguageName}</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.6</DataUri>
<Metadata>
<Item Key="DisplayName">Collect information from user page</Item>
</Metadata>
<LocalizedResourcesReferences MergeBehavior="Prepend">
<LocalizedResourcesReference Language="en" LocalizedResourcesReferenceId="api.selfasserted.en" />
<LocalizedResourcesReference Language="nl" LocalizedResourcesReferenceId="api.selfasserted.nl" />
</LocalizedResourcesReferences>
</ContentDefinition>
<ContentDefinition Id="api.signuporsignin">
<LoadUri>/unified?ui_locales={Culture:LanguageName}</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:unifiedssp:2.1.9</DataUri>
<Metadata>
<Item Key="DisplayName">Sign-up or sign-in page</Item>
</Metadata>
<LocalizedResourcesReferences MergeBehavior="Prepend">
<LocalizedResourcesReference Language="en" LocalizedResourcesReferenceId="api.selfasserted.en" />
<LocalizedResourcesReference Language="nl" LocalizedResourcesReferenceId="api.selfasserted.nl" />
</LocalizedResourcesReferences>
</ContentDefinition>
Self-asserted technical profile for local account sign-in:
<TechnicalProfile Id="FORM-Email">
<DisplayName>Form to enter email</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>
<Item Key="setting.showCancelButton">false</Item>
<Item Key="setting.showSignupLink">false</Item>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
<Item Key="setting.retryLimit">xxx</Item>
</Metadata>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="GetLocalizedStringsForEmail" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" DefaultValue="{OIDC:LoginHint}" AlwaysUseDefaultValue="true" />
<InputClaim ClaimTypeReferenceId="userLanguage" DefaultValue="{Culture:RFC5646}" AlwaysUseDefaultValue="true" />
<InputClaim ClaimTypeReferenceId="sendGridSubject" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="userStatus" />
<OutputClaim ClaimTypeReferenceId="skipUserRegistration" />
<OutputClaim ClaimTypeReferenceId="lastName" />
<OutputClaim ClaimTypeReferenceId="country" />
<OutputClaim ClaimTypeReferenceId="idType" />
<OutputClaim ClaimTypeReferenceId="skipUpdateHighVolumeApp" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="skipToUpdateSSOUserName" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="sendGridSubject" DefaultValue="{Claim:sendGridSubject}" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="ChangeSignInNameToLower" />
</OutputClaimsTransformations>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddressAndSkipIfClaimsPrincipalDoesNotExist" />
<ValidationTechnicalProfile ReferenceId="REST-CheckEmail">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>idType</Value>
<Value>local</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<ValidationTechnicalProfile ReferenceId="OTP-Generate">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>skipEmailValidation</Value>
<Value>True</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<ValidationTechnicalProfile ReferenceId="OTP-SendEmail">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>skipEmailValidation</Value>
<Value>True</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
Validation Technical profile:
<TechnicalProfile Id="OTP-SendEmail">
<DisplayName>Use SendGrid's email API</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://api.sendgrid.com/v3/mail/send</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="ClaimUsedForRequestPayload">sendGridReqBody</Item>
<Item Key="ContentDefinitionReferenceId">api.selfasserted</Item>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="BearerAuthenticationToken" StorageReferenceId="B2C_1A_SendGridSecret" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="GetLocalizedStringsForEmail" />
<InputClaimsTransformation ReferenceId="CreateSendGridFromAddress" />
<InputClaimsTransformation ReferenceId="GenerateSendGridRequestBody" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="sendGridReqBody" />
</InputClaims>
</TechnicalProfile>
Claims transformation:
<ClaimsTransformation Id="GetLocalizedStringsForEmail" TransformationMethod="GetLocalizedStringsTransformation">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="sendGridSubject" TransformationClaimType="sendGrid_Subject" />
</OutputClaims>
</ClaimsTransformation>