Override ContentDefinition at RelyingParty Azure AD B2C Custom Policy - azure-ad-b2c

Can I override the <ContentDefinition><LoadUri> in a RP policy?
RP Policy.xml
<BasePolicy>
<TenantId>mytenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TFExts</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ContentDefinitions>
<ContentDefinition Id="api.idpselections-ext-local">
<LoadUri>~/tenant/default/idpSelector.cshtml</LoadUri>
</ContentDefinition>
</ContentDefinitions>
</BuildingBlocks>
TFExts.xml
<BuildingBlocks>
<ContentDefinitions>
<ContentDefinition Id="api.idpselections-ext-local">
<LoadUri>https://localhost:44377/</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:idpselection:1.0.0</DataUri>
<Metadata>
<Item Key="DisplayName">Idp selection page</Item>
<Item Key="language.intro">Sign in</Item>
</Metadata>
</ContentDefinition>
</ContentDefinitions>
</BuildingBlocks>

I was overwriting the wrong ContentDefinition Id. So yes, I can override a <ContentDefinition><LoadUri> in a relying party policy.
<BuildingBlocks>
<ContentDefinitions>
<ContentDefinition Id="api.selfasserted-ext-local">
<LoadUri>https://localhost:44377/</LoadUri>
</ContentDefinition>
</ContentDefinitions>
</BuildingBlocks>

Related

Set up sign-in for multi-tenant Azure Active Directory using custom policies in Azure Active Directory B2C

I have following the tutorial
https://learn.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-azure-ad-multi-tenant?pivots=b2c-user-flow
I have a button showing up and it looks like it works but when I login with a work account I get
Selected user account does not exist in tenant 'Default Directory' and
cannot access the application '' in that tenant. The account needs to
be added as an external user in the tenant first. Please use a
different account.
It seems to work with a gmail account but not another tenant's account.
My question is how do I get it to work with another tenants account
Here are my 3 custom xml files
https://easyupload.io/m/w0gxlj
I tried to reproduce the same in my environment and got the same error as below:
To resolve the error, please try the below:
I created an Azure AD Application and configured redirect URI:
Now, I created a Policy Key like below:
To configure Azure AD as Identity Provider, I added the ClaimsProvider in the TrustFrameworkExtensions.xml file like below:
<ClaimsProvider>
<Domain>testaadb2c01</Domain>
<DisplayName>Common AAD</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AADCommon-OpenIdConnect">
<DisplayName>Common AAD</DisplayName>
<Description>Login with your Contoso account</Description>
<Protocol Name="OpenIdConnect"/>
<Metadata>
<Item Key="METADATA">https://login.microsoftonline.com/testaadb2c.onmicrosoft.com/v2.0/.well-known/openid-configuration</Item>
<!-- Update the Client ID below to the Application ID -->
<Item Key="client_id">CLIENTID</Item>
<Item Key="response_types">id_token</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>
<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_AADAppSecret"/>
</CryptographicKeys>
<OutputClaims>
------
</OutputClaims>
<OutputClaimsTransformations>
------
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<DisplayName>Azure Active Directory</DisplayName>
<Metadata>
<Item Key="ApplicationObjectId">OBJECTID</Item>
<Item Key="ClientId">CLIENTID</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">CLIENTID</Item>
<Item Key="IdTokenAudience">AUDIENCE</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="XXXXXX"/>
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="XXXXXX"/>
</InputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
When I run the custom Policy, I got the login screen successfully like below:
When I tried to login with AzureAD User account, I am able to sign-in successfully like below:

azure-ad-b2c custom policy how to localize Error messages

Hello iam looking for solution to localize error message to below message but seems it is not overriding
1. Added localization to below content definition tried MergeBehavior="Prepend",MergeBehavior="Append" and MergeBehavior="ReplaceAll"
<ContentDefinition Id="api.signuporsignin">
<LoadUri>~/tenant/templates/AzureBlue/unified.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>
<LocalizedResourcesReferences MergeBehavior="Prepend">
<LocalizedResourcesReference Language="en" LocalizedResourcesReferenceId="api.signuporsignin.en" />
</LocalizedResourcesReferences>
</ContentDefinition>
2.Overriding localization using resources api.signuporsignin.en
<Localization Enabled="true">
<SupportedLanguages DefaultLanguage="en" MergeBehavior="ReplaceAll">
<SupportedLanguage>en</SupportedLanguage>
</SupportedLanguages>
<LocalizedResources Id="api.signuporsignin.en">
<LocalizedStrings>
<LocalizedString ElementType="ErrorMessage" StringId="UserMessageIfInvalidPassword">We didn't recognize this username and password combination.</LocalizedString>
</LocalizedStrings>
</LocalizedResources>
</Localization>
Can anyone suggest me any way to localize error messages in azure b2c custom policy(signupsignin)

Custom Policy does not respect email operating mode

When I run my signup_signin custom policy I receive "Sign in with your sign in name" in the UI, however I'd like for this to be an email address sign in. From what I can tell the TrustFrameworkBase has the operatingMode set to email, however I'm not certain why I'm not getting the correct outcome. Do I need to adjust the SelfAsserted-LocalAccountSignin-Email technical profile in some way?
TrustFrameworkBase unmodified from this:
https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack/blob/master/LocalAccounts/TrustFrameworkBase.xml
TrustFrameworkExtensions.xml
<?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_TrustFrameworkExtensions" PublicPolicyUri="http://{Settings:Tenant}/B2C_1A_TrustFrameworkExtensions">
<BasePolicy>
<TenantId>{Settings:Tenant}</TenantId>
<PolicyId>B2C_1A_TrustFrameworkBase</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<!-- Custom Attributes -->
<!-- Seamless Migration -->
<ClaimType Id="extension_RequiresMigration">
<DisplayName>extension_RequiresMigration</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>Migration status for the user's account. If true, reach out to legacy IdP to migrate user.</AdminHelpText>
</ClaimType>
<ClaimType Id="TokenSuccess">
<DisplayName>TokenSuccess</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>Add help text here</AdminHelpText>
</ClaimType>
<ClaimType Id="MigrationRequired">
<DisplayName>MigrationRequired</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>Holds the value of false when the legacy IdP authentication succeeded</AdminHelpText>
</ClaimType>
</ClaimsSchema>
<ContentDefinitions>
<!-- This content definition is to render an error page that displays unhandled errors. -->
<ContentDefinition Id="api.error">
<LoadUri>~/tenant/templates/AzureBlue/exception.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:globalexception:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Error page</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.idpselections">
<LoadUri>~/tenant/templates/AzureBlue/idpSelector.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:providerselection:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Idp selection page</Item>
<Item Key="language.intro">Sign in</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.idpselections.signup">
<LoadUri>~/tenant/templates/AzureBlue/idpSelector.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:providerselection:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Idp selection page</Item>
<Item Key="language.intro">Sign up</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.signuporsignin">
<LoadUri>https://{Settings:BlobStorageAccount}.blob.core.windows.net/{Settings:BlobContainer}/html/sign_in.html</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:unifiedssp:2.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Signin and Signup</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.selfasserted">
<LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Collect information from user page</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.selfasserted.profileupdate">
<LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Collect information from user page</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.localaccountsignup">
<LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Local account sign up page</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.localaccountpasswordreset">
<LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Local account change password page</Item>
</Metadata>
</ContentDefinition>
</ContentDefinitions>
</BuildingBlocks>
<ClaimsProviders>
<!-- Allows for referencing custom attributes in custom policies -->
<!-- https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-custom-attributes#modify-your-custom-policy -->
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<Item Key="ClientId">{Settings:B2CExtensionsAppId}</Item>
<Item Key="ApplicationObjectId">{Settings:B2CExtensionsObjectId}</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- Seamless Migration -->
<ClaimsProvider>
<DisplayName>Local Account SignIn - Read Migration Status</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Get-RequiresMigration-Status-SignIn">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided user ID.</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.emailAddress" Required="true"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId"/>
<OutputClaim ClaimTypeReferenceId="extension_RequiresMigration" DefaultValue="false"/>
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>REST API to communicate with Legacy IdP</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="UserMigrationViaLegacyIdP">
<DisplayName>REST API call to communicate with Legacy IdP</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://{Settings:TIAPIBaseUrl}/SSOAuth</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="email"/>
<InputClaim ClaimTypeReferenceId="password"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="TokenSuccess" DefaultValue="false"/>
<OutputClaim ClaimTypeReferenceId="MigrationRequired"/>
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account SignIn - Write new password and unmark for migration</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-WritePasswordAndFlipMigratedFlag">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true"/>
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId"/>
<PersistedClaim ClaimTypeReferenceId="userPrincipalName" />
<PersistedClaim ClaimTypeReferenceId="displayName" />
<PersistedClaim ClaimTypeReferenceId="password" PartnerClaimType="password"/>
<PersistedClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration, DisableStrongPassword" AlwaysUseDefaultValue="true"/>
<PersistedClaim ClaimTypeReferenceId="MigrationRequired" PartnerClaimType="extension_RequiresMigration"/>
</PersistedClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common"/>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- End Seamless Migration -->
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">{Settings:ProxyIdentityExperienceFrameworkAppId}</Item>
<Item Key="IdTokenAudience">{Settings:IdentityExperienceFrameworkAppId}</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{Settings:ProxyIdentityExperienceFrameworkAppId}" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="{Settings:IdentityExperienceFrameworkAppId}" />
</InputClaims>
</TechnicalProfile>
<!-- Seamless Migration -->
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_RequiresMigration"/>
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="Get-RequiresMigration-Status-SignIn" ContinueOnError="false"/>
<ValidationTechnicalProfile ReferenceId="UserMigrationViaLegacyIdP" ContinueOnError="false">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_RequiresMigration</Value>
<Value>False</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<ValidationTechnicalProfile ReferenceId="AAD-WritePasswordAndFlipMigratedFlag" ContinueOnError="false">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>TokenSuccess</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<ValidationTechnicalProfile ReferenceId="login-NonInteractive"/>
</ValidationTechnicalProfiles>
</TechnicalProfile>
<!-- End Seamless Migration-->
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<!--UserJourneys>
</UserJourneys-->
</TrustFrameworkPolicy>
SignUpOrSignin.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="{Settings:Tenant}" PolicyId="B2C_1A_SignUp_Signin" PublicPolicyUri="http://{Settings:Tenant}/B2C_1A_SignUp_Signin" UserJourneyRecorderEndpoint="urn:journeyrecorder:applicationinsights" DeploymentMode="{Settings:ApplicationInsightsDeploymentMode}">
<BasePolicy>
<TenantId>{Settings:Tenant}</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<UserJourneyBehaviors>
<JourneyInsights TelemetryEngine="ApplicationInsights" InstrumentationKey="{Settings:ApplicationInsightsInstrumentationKey}" DeveloperMode="{Settings:ApplicationInsightsDeveloperMode}" ClientEnabled="false" ServerEnabled="true" TelemetryVersion="1.0.0" />
<ScriptExecution>Allow</ScriptExecution>
</UserJourneyBehaviors>
<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="email" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
To fix email field type, use <UserInputType>EmailBox</UserInputType>.
<ClaimsSchema>
<ClaimType Id="signInName">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<AdminHelpText>Email address that the user can use to sign in.</AdminHelpText>
<UserHelpText>Email address to use for signing in.</UserHelpText>
<UserInputType>EmailBox</UserInputType>
</ClaimType>
</ClaimsSchema>
We will try make this default in the starter pack.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/claimsschema#userinputtype
This seems to be an issue with Page Layout Version 2.1.x as indicated by JasSuri. My current workaround working solution:
<LocalizedString ElementType="UxElement"
StringId="local_intro_generic">Sign in with your email address</LocalizedString>
<LocalizedString ElementType="ClaimType"
ElementId="signInName"
StringId="DisplayName">Email Address</LocalizedString>
Its not perfect, but fine for the moment for me. The main problem is that the field is no longer of type email, which causes some issues (e.g. mobile keyboards, password managers)
edit: Adding the code by JasSuri in order to make this one "complete" answer:
To fix email field type, use <UserInputType>EmailBox</UserInputType>.
<ClaimsSchema>
<ClaimType Id="signInName">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<AdminHelpText>Email address that the user can use to sign in.</AdminHelpText>
<UserHelpText>Email address to use for signing in.</UserHelpText>
<UserInputType>EmailBox</UserInputType>
</ClaimType>
</ClaimsSchema>

Custom policy showing error labels on page load

I saw a question similar here. I'm facing the same issue with my policy too.
<ContentDefinition Id="api.localaccountsignup">
<LoadUri>https://mysite.azurewebsites.net/b2c/signup</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:selfasserted:1.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Local account sign up page</Item>
</Metadata>
<LocalizedResourcesReferences MergeBehavior="Prepend">
<LocalizedResourcesReference Language="en" LocalizedResourcesReferenceId="signup_en" />
</LocalizedResourcesReferences>
</ContentDefinition>
Validation errors are showing on the page load itself. As the solution of the question says hide that using CSS, but is there any other solution to fix this from B2C without using style.?

How to Create a Custom Error Page in Azure AD B2C | Custom Policies

When using custom policies, how can I specify the error page for a <UserJourney>? Based on the values in the TFP base xml file, I would have thought it would be <RecoveryUri> but that is not working.
<ContentDefinition Id="api.signuporsignin-ext-local">
<!-- <LoadUri>~/tenant/default/unified.cshtml</LoadUri> -->
<LoadUri>https://localhost:44377/</LoadUri>
<!-- <RecoveryUri>~/common/default_page_error.html</RecoveryUri> -->
<RecoveryUri>https://localhost:44377/Home/Error</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:unifiedssp:1.0.0</DataUri>
<Metadata>
<Item Key="DisplayName">Signin and Signup</Item>
</Metadata>
</ContentDefinition>
Current Result:
You would want to update the api.error content definition:
<!-- This content definition is to render an error page that displays unhandled errors. -->
<ContentDefinition Id="api.error">
<LoadUri>~/tenant/default/exception.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:globalexception:1.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Error page</Item>
</Metadata>
</ContentDefinition>

Resources