Thursday, May 18, 2023

Azure AD B2C Custom Policies: Include User EmployeeId in Claims on an UserJourney

In this post, let's see how we can include EmployeeId in Azure AD B2C User Profile in Claims for an Azure AD B2C Sign-In (it could be any actually) UserJourney.
AAD B2C User Profile - Employee ID
Unfortunately, AAD-UserReadUsingObjectId technical profile only retrieves the basic User Profile information and EmployeeId is not part of it (Read User profile attributes).

So in order to retrieve these additional properties what we need to do is update our UserJourney by adding another OrchestrationStep to retrieve users' EmployeeId by calling Microsoft Graph API. But an important thing to note here is, users cannot obtain tokens for Microsoft Graph API using delegated permissions (Read Working with MSAL.js and Azure AD B2C). 

In that case, we can create a simple Minimal API or a HttpTriggered Azure Function that accepts users' ObjecteId and from there, call Microsoft Graph API using Client-Credentials and retrieve the users' EmployeeId. You can have a look at this Minimal API for an example on how to call Graph API using Client-Credentials 

For simplicity, let's say we have the following endpoint.
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

WebApplication app = builder.Build();
app.UseHttpsRedirection();

app.MapPost("/claims", (GetClaimsRequest getClaimsRequest) =>
{
    // TODO: Get claims for the user by calling Graph API using ClientCredentials

    return new
    {
        employeeId = "EMP101"
    };
});

app.Run();

public class GetClaimsRequest
{
    public string ObjectId { getset; }
}
Now the first thing to do is modify our B2C_1A_TrustFrameworkExtensions and define a ClaimType for employeeId.
<ClaimsSchema>
  ...
  <ClaimType Id="employeeId">
    <DisplayName>Employee Id</DisplayName>
    <DataType>string</DataType>
  </ClaimType>
</ClaimsSchema>

And then register a Restful ClaimsProvider to call our REST endpoint and pass the objectId.

<ClaimsProviders>
  ...
  <ClaimsProvider>
    <DisplayName>Get Additional Claims via REST</DisplayName>
    <TechnicalProfiles>
      <TechnicalProfile Id="REST-GetAdditionalClaims">
        <DisplayName>Get Additional Claims via REST call and transform claims</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://743q57pk-7047.aue.devtunnels.ms/claims</Item>
          <Item Key="SendClaimsIn">Body</Item>
          <Item Key="AuthenticationType">None</Item>
          <Item Key="AllowInsecureAuthInProduction">true</Item>
        </Metadata>
        <InputClaims>
          <InputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="objectId" />
        </InputClaims>
        <OutputClaims>
          <OutputClaim ClaimTypeReferenceId="employeeId" PartnerClaimType="employeeId"/>
        </OutputClaims>
        <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
      </TechnicalProfile>
    </TechnicalProfiles>
  </ClaimsProvider>
</ClaimsProviders>

Here, I am mapping the employeeId that is being returned from the API to ClaimTypeReferenceId="employeeIdthat we created before.

The last step is to modify our UserJourney and add an OrchestrationStep to call the REST endpoint before sending the claims.
<OrchestrationSteps>
  ...
  <OrchestrationStep Order="4" Type="ClaimsExchange">
    <ClaimsExchanges>
      <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
    </ClaimsExchanges>
  </OrchestrationStep>
  <OrchestrationStep Order="5" Type="ClaimsExchange">
    <ClaimsExchanges>
      <ClaimsExchange Id="RESTGetAdditionalClaims" TechnicalProfileReferenceId="REST-GetAdditionalClaims" />
    </ClaimsExchanges>
  </OrchestrationStep>
  ...
</OrchestrationSteps>
And that's about it. In my example, I am using SAML authentication, and I can see employeeId is now included in the SAML response.
EmployeeId Included In Claims
And that's about it.

Hope this helps.

Happy Coding.

Regards,
Jaliya

No comments:

Post a Comment