Is there a way to use Key Vault for custom policy? - azure-ad-b2c

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.

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.

B2C Sign-up screen shows {OIDC:LoginHint} instead of the login

I am passing an email of a prospective member in the login_hint from my website to B2C. In my custom policy I am setting the email claim of the "SignUp" TechnicalProfile to {OIDC:LoginHint}
<TechnicalProfile Id="CustomLocalAccountSignUpWithLogonEmail">
<DisplayName>Email 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>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" DefaultValue="{OIDC:LoginHint}" />
</InputClaims>
<OutputClaims>
But instead of seeing the user's email, the string {OIDC:LoginHint} is displayed in the form:
There is a similar question from 2018 with a suggested workaround of using JavaScript to populate the email field on the Sign Up form. But, I don't use custom templates, so the JavaScript workaround won't work for me.
All I need is to populate the email claim with the value passed in {OIDC:LoginHint}. Is there any way to solve this in the policy XML?
Thank you
In a selfAsserted technical profile, you must:
The IncludeClaimResolvingInClaimsHandling metadata must be set to true.
The input or output claims attribute AlwaysUseDefaultValue must be set to true.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/claim-resolver-overview#using-claim-resolvers
An example of using both settings is here
https://learn.microsoft.com/en-us/azure/active-directory-b2c/claim-resolver-overview#restful-technical-profile

Getting access token for an api protected by B2C, using custom policies

I have an api that is protected using ADB2C authentication. I need to call this api via custom policies. I followed the documentation enter link description here and have added the two technical profiles as validation technical profile of a self asserted profile.
I am getting an access token returned by the below technical profile :
<TechnicalProfile Id="SecureREST-AccessToken">
<DisplayName></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://login.microsoftonline.com/{tenant id here}/oauth2/v2.0/token</Item>
<Item Key="AuthenticationType">Basic</Item>
<Item Key="SendClaimsIn">Form</Item>
</Metadata>
<CryptographicKeys>
<Key Id="BasicAuthenticationUsername" StorageReferenceId="B2C_1A_SecureRESTClientId" />
<Key Id="BasicAuthenticationPassword" StorageReferenceId="B2C_1A_SecureRESTClientSecret" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="client_credentials" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="{app id uri for protected resource}/.default" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="bearerToken" PartnerClaimType="access_token" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
And then making the rest api call using below profile :
<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://99a0a14a6402.ngrok.io/api/Identity/SignUpAsync
</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="SendClaimsIn">Header</Item>
<Item Key="AllowInsecureAuthInProduction">false</Item>
<Item Key="UseClaimAsBearerToken">bearerToken</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="bearerToken"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="phonePresent"/>
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
However, scopes are missing from the returned access token, hence token validation is failing on the api.
Is my call to get access token missing anything?
For the client credentials grant flow, the API permissions must be created as roles (see How to: Add app roles to your application and receive them in the token) and then granted admin consent (see Admin consent button).
As result, the bearer token contains the roles claim, rather than the scp claim.
The API application checks access using this roles claim (see Verify app roles in APIs called by daemon apps).

How to set SendClaimsIn for azure ad b2c REST call

I have created an AAD B2C custom policy which makes a call to call our REST API when a new user signs up by creating a custom Azure AD B2C custom policy.But i have to set two values to REST API. Ocp-Apim-Subscription-Key in header and email id in body.but i have to set SendClaimsIn only as either header or body.
so i added SendClaimsIn as header.But i cdont know how to set both values as inputclaim.My code is
<ClaimsProvider>
<DisplayName>Signup REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-ValidateProfile">
<DisplayName>Check loyaltyId 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://myapicall.io/api/</Item>
<Item Key="SendClaimsIn">Header</Item>
<Item Key="AuthenticationType">Bearer</Item>
<Item Key="AllowInsecureAuthInProduction">false</Item>
</Metadata>
<CryptographicKeys>
<Key Id="BearerAuthenticationToken" StorageReferenceId="B2C_1A_RestApiBearerToken" />
</CryptographicKeys>
<InputClaims>
<!-- Claims sent to your REST API -->
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="grant_type" "DefaultValue"="Ocp-Apim-Subscription-Key"/>
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
its shows validation error while uploading
makes a reference to ClaimType with id "Ocp-Apim-Subscription-Key" but
neither the policy nor any of its base policies contain such an
element
i want to set header as Ocp-Apim-Subscription-Key as "12345"
Add the following inside of the <ClaimsSchema> tag near the top of the file:
<ClaimType Id="Ocp-Apim-Subscription-Key">
<DisplayName>OCP APIM Subscription Key</DisplayName>
<DataType>string</DataType>
</ClaimType>
Change the values inside of the <InputClaims> in your REST-ValidateProfile technical profile to the following:
<InputClaims>
<!-- Claims sent to your REST API -->
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="Ocp-Apim-Subscription-Key" DefaultValue="12345" />
</InputClaims>
You don't need a grant_type input claim for a static OAuth2 bearer (see here).

Howto Enable Email Verification in Azure AD B2C

How do I enable email verification in B2C w/ custom policies?
In an attempt to reverse engineer it, I tried disabling it in a built-in policy and downloaded the policy. I tried adding that metadata item to my self-asserted technical profile but that didn't work.
Reverse Engineer Test | Built-In Policy
<TechnicalProfile Id="SelfAsserted-Input">
<Metadata>
<Item Key="EnforceEmailVerification">False</Item>
</Metadata>
</TechnicalProfile>
My Self-Asserted Technical Profile
<TechnicalProfile Id="LocalAccountSignUp">
<DisplayName>User signup</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="EnforceEmailVerification">True</Item>
</Metadata>
...
</TechnicalProfile>
I'm using usernames for local accounts incase that matters.
Whether a local account is created with an email address- or user name-based sign-in name, you add email verification by adding PartnerClaimType="Verified.Email" to the "email" output claim of your self-asserted technical profile, as follows:
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />

Resources