Integrate API in SignUp Policy for validating User Input - azure-ad-b2c

I have an existing API deployed to Azure Portal.
For Ex: https://Edudev.azure-api.net/validate?param1=value
API response is a JSON object,
For Ex:
{
"Message": "Please provide details\r\n",
"NeedInfo": "true"
}
we need to pass subscription key in the header whenever we call this api
My Question is:
Can I call this api in SignUp Policy Xml in Azure AD B2C user journey as validation of user input and display the message returned in response
Thanks,

A REST API can be invoked from a custom policy as described at "Integrate REST API claims exchanges in your Azure AD B2C user journey as validation of user input".
You can secure the REST API using either HTTP basic authentication or client certificate authentication.
You can otherwise pass the subscription key in the request query string.
An input claim, such as param1, can be passed in:
The request query string
The request headers
The request body as application/x-www-form-urlencoded
The request body as application/json
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-API-Validate">
<DisplayName>Validate REST 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://edudev.azure-api.net/validate?subscription-key=...</Item>
<Item Key="AuthenticationType">None|Basic|ClientCertificate</Item>
<Item Key="SendClaimsIn">Body|Form|Header|QueryString</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="param1" />
</InputClaims>
<OutputClaims>
...
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
The REST API must return a 409 Conflict response in order for messages to be shown to users:
HTTP/1.1 409 Conflict
{
"version": "1.0.0",
"status": 409,
"userMessage": "Please provide details."
}

Related

Is there a way to use Key Vault for custom policy?

In my custom policy I have a technical profile to call SendGrid API to send emails.
<TechnicalProfile Id="SendEmail">
<DisplayName>Use SendGrid's email API to send the code the the user</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">send-grid-url</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="ClaimUsedForRequestPayload">emailRequestBody</Item>
</Metadata>
<CryptographicKeys>
<Key Id="BearerAuthenticationToken" StorageReferenceId="B2C_1A_SendGridKey" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="GenerateEmailRequestBody" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="emailRequestBody" />
</InputClaims>
</TechnicalProfile>
Originally the API key for SendGrid is uploaded in the Policy Keys in the Identity Experience Framework and named B2C_1A_SendGridKey. However we want to hold all our keys and secrets in the Key Vault instead of uploading it in the Policy Keys. Is there a way to get the key from Key Vault and use it in the custom policy?
No, unfortunately not.
B2C requires that its secrets be stored as Policy Keys, which is what you have here, and there's no way around that.
Even if you wrapped the actual SendGrid call in your own API so that you could store the SendGrid secret in KeyVault you'd still have to secure your API call and that would require some secret held in B2C Policy Keys.

Add Role to Existing User in Azure AD B2C using Graph SDK

We are using GRAPH SDK to manage users In Azure AD B2C.
We have been able to create an user account and now want to assign a role to the user.
We have referred the documentation but not able to find the way forward.
Is there a way we can assign role to a user using Graph SDK
Any help on this would be appreciated
Azure AD B2C does not really support roles out of the box sadly. There are a couple of ways to do this. If you want to add the roles to your token on sign-in, then you will have to use custom policies and an API call (or Azure Function) to add roles to your claims. If you are new to custom policies, I highly recommend reading the official documentation on custom policies first.
If you only want to manage roles without adding them to your token on sign-in. You can create Security groups in your ActiveDirectory (In the same directory as your B2C tenant). This should look something like this.
You can then use the Get Groups call. or list memberOf call.
GET /groups/{id}
GET /me/memberOf
GET /users/{id | userPrincipalName}/memberOf
Ofcourse you can also add users to the groups
POST /groups/{group-id}/members/$ref
If you do want to add the roles to your token on sign-in, I personally used the video. https://www.youtube.com/watch?v=C9qN6QqnxQ8 This uses an Azure function but you can also use your own API if you want to.
IMPORTANT: You cannot test calls to your own API with localhost in custom policies, so make sure to make it available with something like ngrok.
I will describe the steps as well: First add the role (or roles) ClaimTypeReferenceId to your SignUp.xml
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" />
Other claims
<OutputClaim ClaimTypeReferenceId="role" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
In the TrustFrameworkExtensions.xml add the claimtype role (or roles)
<ClaimType Id="role">
<DisplayName>Role</DisplayName>
<DataType>stringCollection</DataType> <!-- or string if you only want 1 role -->
</ClaimType>
Add the Rest API call to your TrustFrameworkExtensions.xml. This sets the Parameters of your API call to an email address which you have received from the sign-in, and it expects a role as a result.
<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>
<!-- Set the ServiceUrl with your own REST API endpoint -->
<Item Key="ServiceUrl">https://functions.azurewebsites.net/api/HttpTriggerGetRoles?code=YOURCODE</Item> <!-- TODO CHANGE THIS URL to your own-->
<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="signInNames.emailAddress" PartnerClaimType="emails" />
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
</InputClaims>
<OutputClaims>
<!-- Claims parsed from your REST API -->
<OutputClaim ClaimTypeReferenceId="role" PartnerClaimType="role"/>
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
Lastly, add the GetRoleData TechnicalProfile to your second to last OrchestrationStep
<OrchestrationStep Order="10" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="GetRoleData" TechnicalProfileReferenceId="REST-GetProfile" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="11" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
For testing I created an Azure Function with the following code in C#. In this example I just put it hard coded. But you could make a call to the graph API from here.
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static IActionResult Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string[] roles = {"Admin", "Employee"};
var adminrole = new {
role = roles
};
return new OkObjectResult(adminrole);
}
// string email = req.Query["email"];
// string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
// dynamic data = JsonConvert.DeserializeObject(requestBody);
// email = email ?? data?.email;
Your token should now contain the roles.
{
"typ": "JWT",
"alg": "RS256",
"kid": "H-0"
}.{
"iss": "https://demo.b2clogin.com/9729/v2.0/",
"exp": 164,
"nbf": 1634,
"aud": "4a12ea396",
"given_name": "Tim C",
"family_name": "C",
"name": "Tim",
"idp": "google.com",
"sub": "adc1",
"role": [
"Admin",
"Employee"
],
"scp": "demo.write demo.read",
"ver": "1.0",
}.[Signature]

Is there a way to display data returned from an API REST response in Azure AD B2C?

I'm working on a password reset flow and I need to display a custom message returned by an API response, but I can't find a way to display this message. I've tried the following.
<ClaimType Id="userMessage">
<DisplayName>userMessage</DisplayName>
<DataType>string</DataType>
<UserInputType>Paragraph</UserInputType>
</ClaimType>
<TechnicalProfile Id="SendOtp">
<DisplayName>Send Otp</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">SomeUrl</Item>
<Item Key="AuthenticationType">ApiKeyHeader</Item>
<Item Key="SendClaimsIn">Body</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="userMessage" />
</OutputClaims>
</TechnicalProfile>
But it doesn't show anything.
Have your API return a HTTP 409 error response code. The flow will not continue after this point, until your API responds with HTTP 200 upon the user submitting the page again.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/restful-technical-profile#error-handling
You cannot display a message from the API, unless the API responds with HTTP 409 error status code.
If you want to display a message on success, then localise the message for the display control for send code:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/localization-string-ids#verification-display-control-user-interface-elements

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.

Resources