How to customize the error page for REST API Failures in Azure AD B2C custom policy? - azure-ad-b2c

Customizing the error page for REST API Failures which we are calling in the Orchestration Steps :
We have a scenario where we need to call the Rest API before any Self asserted Page being presented to the user
<OrchestrationStep Order="5" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="Rest-API-Call" TechnicalProfileReferenceId="Some-Rest API " />
</ClaimsExchanges>
</OrchestrationStep>
<TechnicalProfile Id="Some-Rest API">
<DisplayName>REST call</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://{url}</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="SendClaimsIn">Url</Item>
<Item Key="UseClaimAsBearerToken">token</Item>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
<Item Key="AllowInsecureAuthInProduction">false</Item>
<Item Key="DefaultUserMessageIfRequestFailed">Unable to Process your Request</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="token" />
<InputClaim ClaimTypeReferenceId="objectId" />
</InputClaims>
</TechnicalProfile>
Whenever this API Fails with status code 4xx and 5xx, we will be getting the default error message which Microsoft is providing
Sorry, We are having trouble signing you in. We track these errors automatically , but if the problem persists feel free to contact us. In the meantime, please try again...
We are getting this Page, Could you please help us is there any way we can customize this error Page, with some default exception html Page
where we can show our customized page instead of this default page.

The default API error page is defined in the content definition api.error if you started from one of the custom policy starter packs. You can modify that content definition to point to your own HTML page so you can customize the styling. Here's a link to that doc: https://learn.microsoft.com/en-us/azure/active-directory-b2c/customize-ui-with-html?pivots=b2c-custom-policy
api.error however only displays on unhandled exceptions which might not cover your use case.
An answer to another post (Error handling in Azure B2C Custom Policy REST Call) from someone on the engineering team states that anything but a 200 from an API halts the journey execution and returns an error to the app immediately, so unless you put the rest call in a validation tech profile, this is the behavior you're going to get.

Related

How to avoid hardcoding ServiceUrl in AD B2C custom policy API connector?

I have a REST API connector in my B2C custom policy. All examples I have seen hard code the value of the ServiceUrl metadata item in the REST technical profile. For example:
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-MyApiConnector">
<DisplayName>This an example of my API connector</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://someapi.com/api/endpoint</Item>
<Item Key="SendClaimsIn">Url</Item>
<Item Key="AuthenticationType">Basic</Item>
<Item Key="AllowInsecureAuthInProduction">false</Item>
</Metadata>
<CryptographicKeys>
<Key Id="BasicAuthenticationUsername" StorageReferenceId="B2C_1A_RestApiUsername" />
<Key Id="BasicAuthenticationPassword" StorageReferenceId="B2C_1A_RestApiPassword" />
</CryptographicKeys>
<InputClaims>
</InputClaims>
<OutputClaims>
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
I have seen a snippet on a GitHub issue (https://github.com/MicrosoftDocs/azure-docs/issues/58267) where the poster seems to have pulled the ServiceUrl from some external settings:
<Item Key="ServiceUrl">{Settings:CID-ApiUrl}/{user_id}</Item>
I can't seem to find any further information on this. Is there a way to store things like the ServiceUrl in some external settings that can then be accessed within the custom policy to avoid having to hardcode the URL?
Loading the ServiceUrl dynamically is not possible once it is deployed to B2C. But there is a very useful VS Code extension which should help during development. Go to the policy settings section from the following documentation: Azure AD B2C Tools for VSCode
In short, it helps to define variables, and replace them based on values stored in an appsettings.json file. You can have multiple sets of settings for different environments. Then use a single command to replace all variables and get the final policies to another folder.
You can then deploy them to your b2c tenant. Another option is to replace the variables from a CI/CD pipelines and deploy them to the tenant.
It’s not dynamic. They would be using CICD pipeline to populate them before uploading into the tenant.

Looking to have Azure B2C custom policy use a custom error page (and pass a value)

UPDATE:
I am able to finally get my custom error page to render. I had to use
<div id="api"></div>
in my view.
My last concern here, which seems impossible, is adding the value of one of my claims (from the idToken passed to my B2C policy when I call it) into the LoadUri of my ContentDefintion.
Here are my TechnicalProfiles:
<TechnicalProfile Id="SelfAsserted-SigningNotReadyError">
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="GetUser" />
</InputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="SelfAsserted-UserError" />
</TechnicalProfile>
<TechnicalProfile Id="SelfAsserted-UserError">
<DisplayName>Unsolicited error message</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.signingerrorlink</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="errorMessage" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="errorMessage" />
</OutputClaims>
</TechnicalProfile>
and my ContentDefinition
Note: see how I have to hardcode a GUID. :(
signingerror?user=7ec00437-1ad9-4acc-a285-a729003ca99d
<ContentDefinition Id="api.signingerrorlink">
<LoadUri>https://something.azurewebsites.net/signingerror?user=7ec00437-1ad9-4acc-a285-a729003ca99d</LoadUri>
<RecoveryUri>https://something.azurewebsites.net/error</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Signing Not Ready</Item>
</Metadata>
</ContentDefinition>
ORIGINAL:
I'm in a pickle here. I have been looking through many posts in stackoverflow and over the internet in general. I hope you can help me.
I have a custom B2C policy, which basically accepts a token, performs some functions then returns an access token in the end. That works fine. My issue is that I need to define a step which will trigger a techical profile based on a precondition. The precondition actually works well to.
What I am missing is a way to construct a technicalprofile/contentdefinition to load my own error page (.Net Core 3 controller) in another project but totally accessible (Controllers/SigningErrorController).
My step:
<OrchestrationStep Order="7" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>isReadyForSigning</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-SigningNotReady" TechnicalProfileReferenceId="SelfAsserted-SigningNotReadyError" />
</ClaimsExchanges>
</OrchestrationStep>
Part 2! I need to pass a value to this custom error page. A guid in this case.
If I use the TP for invalidlink then this works. I have tried various custom contentdefinitions similar to:
<ContentDefinition Id="api.signingerrorlink">
<LoadUri>https://something.azurewebsites.net/error</LoadUri>
<RecoveryUri>https://something.azurewebsites.net/signingerror</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:globalexception:1.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Error page</Item>
</Metadata>
</ContentDefinition>
...with the signingerror being the custom error page I want to go to.
If I run this, the policy just skips over the step completely. If I use the invalid link TP, then it works. I've tried to figure out also how to pass a value to the error page. I'm at a loss folks.
Any help you can give would be much appreciated.
You can use a claim resolver in the LoadUri element. E.g.:
<ContentDefinition Id="api.signingerrorlink">
<LoadUri>https://something.azurewebsites.net/signingerror?user={Claim:packageUserId}</LoadUri>
<RecoveryUri>https://something.azurewebsites.net/error</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Signing Not Ready</Item>
</Metadata>
</ContentDefinition>
https://learn.microsoft.com/en-us/azure/active-directory-b2c/claim-resolver-overview#content-definition

How Do I Whitelist Email Domains In Signup With Azure B2C?

I am using simple email signup for Azure B2C and not SSO and my user flow is the basic one from this tutorial
https://learn.microsoft.com/en-ca/azure/active-directory-b2c/tutorial-create-user-flows
At the moment I am using temporary email addresses for my testing, however, when I go live I want to whitelist the domains that people can sign up with.
How do I do this whitelisting?
Currently Using Azure AD B2C Userflows we can't Whitelist the users while signup based on the email domain. We Need to handle it using Custom policies by calling the Rest API and need to validate the email address.
Please go through the documents and sample on using Custom policies with Rest API.
A Restful technical profile provides support for interfacing with your own RESTful service. Azure AD B2C sends data to the RESTful service in an InputClaims collection and receives data back in an OutputClaims collection. Find the ClaimsProviders element in your
Configure the REST API technical profile
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-GetProfile">
<DisplayName>Get user extended profile Azure Function web hook</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://your-account.azurewebsites.net/api/GetProfile?code=your-code</Item>
<Item Key="SendClaimsIn">Body</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="objectId" />
<InputClaim ClaimTypeReferenceId="userLanguage" PartnerClaimType="lang" DefaultValue="{Culture:LCID}" AlwaysUseDefaultValue="true" />
</InputClaims>
<OutputClaims>
<!-- Claims parsed from your REST API -->
<OutputClaim ClaimTypeReferenceId="balance" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
You need to handle the Email validation logic in your REST API. The above Provideed document links helps you in building the custom policy.
This limitation is already raised in the Azure Feedback uservoice and to consider this feature request for the future, please upvote on the existing UserVoice.
That way the product group can prioritize this accordingly.

B2C: Localization for messages originating from REST API

I am currently implementing this example to use an rest API during the user registration.
The basic idea is that the API throws an 409 Conflict error to interrupt the registration.
// Can I return a special "StringId" or something here for localization?
return new ConflictObjectResult(new B2CResponseModel($"A verification email sent to you. Please open your mail box and click on the link. If you didn't receive the email, please click on the 'Send verification email' button.", HttpStatusCode.Conflict));
I want to show the user a message that is localized to their current language. I would prefer to do the localization within the custom policies, but I would also accept a solution within the API (would need to get the User Language for this).
Is there a way to do this localization? Like returning a StringId via API and using this within the policy?
I am also considering not returning an error from the API, to show the message in a new screen instead (like How to display error returned from custom REST API endpoint in a subsequent orchestration step?). However, localization options for this elude me as well.
In case anybody is looking for a way to send the user's locale to the REST API:
https://learn.microsoft.com/nb-no/azure/active-directory-b2c/claim-resolver-overview
<TechnicalProfile Id="REST-API-SendVerificationEmail">
<DisplayName>Sign-Up send link</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://xxxx</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="userLanguage" DefaultValue="{Culture:LanguageName}" />
<InputClaim ClaimTypeReferenceId="policyId" PartnerClaimType="policy" DefaultValue="{Policy:PolicyId}" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="{OIDC:scope}" />
<InputClaim ClaimTypeReferenceId="clientId" DefaultValue="{OIDC:ClientId}" />
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
Hope this is similar to this
See the answer by Jas Suri. Pass the localisation parameter to API and return the localised message or can return an error code and based on that display translated message using policy itself.

Reading Extension Claims in Azure AD B2C

I have 2 claims that I want to store in the Directory for my application to use. These are not available for the user to edit however is available for the application to read from the Token.
The BuiltIn policies are able to retrieve the claims however, I have not had any success with retrieving these claims using Custom Policies.
Reading through Next Steps of the article “Creating and using custom attributes in a custom profile edit policy” the claims will need to be added to the RP and TechnicalProfile to read from Directory. I accordingly updated the RP and as well the TP's that read from Directory such as
<TechnicalProfile Id="AAD-UserReadUsingAlternativeSecurityId">
<TechnicalProfile Id="AAD-UserReadUsingObjectId">
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<TechnicalProfile Id="AAD-UserReadUsingEmailAddress">
Unable to figure out what might be missing to retreive the 2 extension claims.
Assuming you are reading the custom claims in the user journeys and writing them via the Azure AD Graph API, then you must:
1: Add the custom claims as <ClaimType />s to the base policy.
<ClaimType Id="extension_UserAttribute1">
<DisplayName>User Attribute 1</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="extension_UserAttribute2">
<DisplayName>User Attribute 2</DisplayName>
<DataType>string</DataType>
</ClaimType>
2: Add the application and object identifiers for the extensions app to the "AAD-Common" technical profile which is required to read the custom claims from the Azure AD B2C directory.
<TechnicalProfile Id="AAD-Common">
<DisplayName>Azure Active Directory</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.AzureActiveDirectoryProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ApplicationObjectId">Insert the object identifier for the b2c-extensions-app application here</Item>
<Item Key="ClientId">Insert the application identifier for the b2c-extensions-app application here</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="TokenSigningKeyContainer" />
</CryptographicKeys>
...
</TechnicalProfile>
Note: If you are wanting to read the custom claims in both built-in policies and custom policies, then you must use the application and object identifiers for the built-in b2c-extensions-app application rather than a custom extensions app as suggested by the Azure Active Directory B2C: Creating and using custom attributes in a custom profile edit policy tutorial.
3: Add the custom claims as <OutputClaim />s to the following technical profiles:
"AAD-UserReadUsingObjectId" for local account sign-in and profile editing
"AAD-UserReadUsingAlternativeSecurityId" for a social account sign-in and profile editing
"LocalAccountDiscoveryUsingEmailAddress" and "AAD-UserReadUsingEmailAddress" for a local account password reset
<OutputClaims>
...
<OutputClaim ClaimTypeReferenceId="extension_UserAttribute1" />
<OutputClaim ClaimTypeReferenceId="extension_UserAttribute2" />
</OutputClaims>
4: Issue the custom claims as <OutputClaim />s in any relying party policies.
Thanks #ChrisPadget. For anybody still struggling. Make sure that the UserJourney Step that reads data from AD is actually available in your User Journey. In my case, I had to add:
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>

Resources