Monday, March 11, 2024

Azure AD B2C: Call an External API Using Client Credentials in an User Journey

In this post, let's see how to call an external API using Client Credentials in an Azure AD B2C User Journey.

I am assuming Azure AD B2C App Registration is already set up for the client app with the necessary permission  (scope access) to call the protected API and you have noted down the Client ID, Client Secret, and the Scope.

Note: There are no additional actions to enable the client credentials for user flows or custom policies. Both Azure AD B2C user flows and custom policies support the client credentials flow by default. But of course, you can create a custom policy to customize the user journey of the OAuth 2.0 Client credentials and extend the token issuance process.

First, you can test that everything is set up correctly using the following Powershell script.

$clientId = "<clientId>"
$clientSecret = "<clientSecret>"
$endpoint = "https://<tenant-name>.b2clogin.com/<tenant-name>.onmicrosoft.com/<policy>/oauth2/v2.0/token"
$scope = "<scope>"
$body = "grant_type=client_credentials&scope=" + $scope + "&client_id=" + $clientId + "&client_secret=" + $clientSecret

$token = Invoke-RestMethod -Method Post -Uri $endpoint -Body $body
$token | ConvertTo-Json

Here the scope is something like follows:

$scope = "https://<tenant-name>.onmicrosoft.com/45a2252d-099a-4c6a-9c57-66eac05e2693/.default"
The script should output something like below.
Test Client Credentials
Now let's see how we can use this in an Azure AD B2C User Journey.

1. Define a ClaimType for access_token.

<BuildingBlocks>
  <ClaimsSchema> ...
    <ClaimType Id="access_token">
      <DisplayName>Access Token</DisplayName>
      <DataType>string</DataType>
    </ClaimType>
  </ClaimsSchema> ... </BuildingBlocks>

2. Define TechnicalProfiles to retrieve access_token and to call the external API using the retrieved access_token.

<ClaimsProvider>
  ...
  <TechnicalProfiles>
    <TechnicalProfile Id="REST-GetClientCredentials">
      <DisplayName>Get Client Credentials</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://<tenant-name>.b2clogin.com/<tenant-name>.onmicrosoft.com/<policy>/oauth2/v2.0/token?grant_type=client_credentials&amp;scope=<scope>&amp;client_id=<clientId>&amp;client_secret=<clientSecret>
        </Item>
        <Item Key="SendClaimsIn">Body</Item>
        <Item Key="AuthenticationType">None</Item>
        <Item Key="AllowInsecureAuthInProduction">true</Item>
      </Metadata>
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="access_token"/>
      </OutputClaims>
      <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop"/>
    </TechnicalProfile>
    <TechnicalProfile Id="REST-CallApiUsingClientCredentials">
      <DisplayName>Call an External API using Client Credentials</DisplayName>
      <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      <Metadata>
        <Item Key="ServiceUrl"><Endpoint to call></Item>
        <Item Key="SendClaimsIn">Header</Item>
        <Item Key="AuthenticationType">Bearer</Item>
        <Item Key="UseClaimAsBearerToken">access_token</Item>
        <Item Key="AllowInsecureAuthInProduction">true</Item>
        <Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
      </Metadata>
      <InputClaims>
        <InputClaim ClaimTypeReferenceId="access_token"/>
      </InputClaims>
      <OutputClaims>
        <!-- Output Claims from Calling the API -->
      </OutputClaims>
      <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
    </TechnicalProfile> ...
  </TechnicalProfiles>
</ClaimsProvider>

3. Finally, introduce additional OrchestrationSteps to your UserJourney to use the above TechnicalProfiles.

<UserJourneys>
  <UserJourney Id="<UserJourneyId>">
    <OrchestrationSteps>
      ...
      <OrchestrationStep Order="7" Type="ClaimsExchange">
        <ClaimsExchanges>
          <ClaimsExchange Id="RESTGetClientCredentials" TechnicalProfileReferenceId="REST-GetClientCredentials" />
        </ClaimsExchanges>
      </OrchestrationStep>
      <OrchestrationStep Order="8" Type="ClaimsExchange">
        <ClaimsExchanges>
          <ClaimsExchange Id="RESTCallApiUsingClientCredentials" TechnicalProfileReferenceId="REST-CallApiUsingClientCredentials" />
        </ClaimsExchanges>
      </OrchestrationStep> ...
      <OrchestrationStep Order="11" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
    </OrchestrationSteps>
  </UserJourney>

Now that should be it.

Hope this helps.

Happy Coding.

Regards,
Jaliya

No comments:

Post a Comment