How to add optional claims to access token when using azure oauth client credential flow

Viewed 721

In my scenario there is a Azure Application Registration (client_app) with credentials. This application is used to request an oauth2 access token. A second Application Registration (main_app) is the scope, which is providing App Roles and more.

My goal is to include information from client_app in a jwt token claim, when requesting the token using the client credential flow on an azure /oauth2/v2.0/token endpoint.

This is a token request:

POST /<tenant id>/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
 
client_id=<Application ID of client_app>
&client_secret=<secret of client_app>
&scope=<Application ID URI of main_app + "/.default">
&grant_type=client_credentials

The response of the token request is a valid token including roles (granted App Roles from main_app to client_app), aud, sub and idtype: app claims. But my optional claims are missing.

MS docs state, that it is possible to include optional claims using directory extensions

Schema and open extensions are not supported by optional claims, only the AAD-Graph style directory extensions. This feature is useful for attaching additional user information that your app can use [...]

So I tried adding a directory extension to my main_app (extension_<main_app ID>_someAttrName), added an optional claim to main_app and stored a value to this directory extension in client_app. Unfortunately the extension value is not reflected in the token.

I tried adding the directory extension on the client_app itself (extension_<client_app ID>_someAttrName) and map the directory extension to main_app by using ClaimsMappingPolicy. Unfortunately the extension value is not reflected in the token.

It seems to me, that the claims are missing, since they do not originate from a user object. MS docs provide a lot of information to add optional claims, but most scenarios involve a user. E.g. the optional claims source property of a manifest:

The source (directory object) of the claim. There are predefined claims and user-defined claims from extension properties. If the source value is null, the claim is a predefined optional claim. If the source value is user, the value in the name property is the extension property from the user object.

As I understand it, only null and "user" is supported. I want to include a directory extension originating from an Application Registration (client_app). And since I have to use the client credential flow (server-to-server) there is no user involvement.

So how do I add custom optional claims to a token when using client credential flow, when no user object is involved? There is an application object involved. So how do I need to configure claims to reflect custom application data (e.g. directory extensions)?

Manifest of main_app:

{
    "id": "<main_app ID>",
    "acceptMappedClaims": true,
    "accessTokenAcceptedVersion": 2,
    "appId": "<main_app APP ID>",
    "appRoles": [{
            "allowedMemberTypes": ["Application"],
            "isEnabled": true,
            "origin": "Application",
            "value": "readAll"
        },{
            "allowedMemberTypes": ["Application"],
            "isEnabled": true,
            "origin": "Application",
            "value": "writeAll"
        }],
    "identifierUris": ["api://<main_app ID>"],
    "optionalClaims": {
        "idToken": [],
        "accessToken": [            {
                "name": "extension_<main_app ID>_someAttrName",
                "source": "application", <-- propably invalid, "user" or null is supported
                "essential": false,
                "additionalProperties": []
            }, { ... }
        ],
    },
} /# trunced for readability

Manifest of client_app:

{
    "id": "<client_app ID>",
    "accessTokenAcceptedVersion": null,
    "appId": "<client_app APP ID>",
    "passwordCredentials": [{...}],
    "extension_<main_app ID>_someAttrName": "some-string-value"
} /# trunced for readability

My ClaimsMappingPolicy definition looks like this:

"ClaimsMappingPolicy": { "Version": 1, "IncludeBasicClaimSet": "true",
        "ClaimsSchema": [ {
            "Source": "application",
            "ExtensionID": "extension_<client_app ID>_someAttrName",
            "JwtClaimType": "someAttrName"
        } ]
}
1 Answers

I reached out to Azure support engineers, and they told me, that it's not possible to include directory extension based claims originating from Application Registrations. They did not go into detail but replied this:

I apologize to inform we got to know that reflecting extension claims in JWT token won't be possible. Since directory extensions can't be added to servicePrincipal objects.

I'm assuming the requesting entity is a service principal when using the client credentials flow.

Related