I am working in Azure AD B2C to add custom extensions per application. Theses extensions must be returned in the jwt when the login is requested by the application.
So I create the extension on the app using the graph api
POST https://graph.microsoft.com/v1.0/applications/{{appid}}/extensionProperties
{
"name": "name",
"dataType": "String",
"targetObjects": [
"User"
]
}
Then I associate a value for a specific user
PATCH https://graph.microsoft.com/v1.0/users/{{userid}}
{
"extension_{{appid(without dashes}}_name": "1234"
}
Now I go on the app manifest to add the optional claim.
"optionalClaims": {
"idToken": [
{
"name": "extension_{{appid(without dashes}}_name",
"source": "user",
"essential": true,
"additionalProperties": []
}
],
"accessToken": [
{
"name": "extension_{{appid(without dashes}}_name",
"source": "user",
"essential": true,
"additionalProperties": []
}
],
"saml2Token": []
},
Save but the claim never appear on the jwt token.
I also tried using the answer of this post but didn't work either.
The problem is you’ve used Optional claims setup, which works for AAD but not AAD B2C.
Follow this: https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-custom-attributes?pivots=b2c-user-flow
If you want to select your custom attribute through the Azure Portal - AAD B2C - User Attributes blade, and the attribute was created via Graph API, you have to recreate it in the Portal for it to reconcile.
You would also need to target the b2c-extensions-app AppId when defining the attribute with Graph API.
I tried to reproduce the same in my environment and got the claims successfully
As Jas Suri - MSFT commented, this will only work if you are adding optional claims to Azure AD application.
I created the extension attribute via Graph API like below:
I associated the above extension attribute to a specific user like below:
Please check whether that extension attribute is visible in optional claims UI or not and add like below:
When you check the manifest, it will be added automatically like below:
I generated the JWT token using auth-code flow via Postman like below:
After decoding the JWT token (ID-Token), I got the claims successfully like below:
Related
I am trying to create GCP workload identity federation with Azure AD service principal but when I am trying to create the initial sts token with GCP I get bad request. I am suspecting that the problem is that my audience currently on jwt token is "https://management.azure.com" where it should be the audience that is set in the GCP WiF configuration. Currently I am getting the Azure AD accesstoken with:
$AzContext = Get-AzContext
$accesstoken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(
$AzContext.'Account',
$AzContext.'Environment',
$AzContext.'Tenant'.'Id',
$null,
[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never,
$null,
'https://management.azure.com/'
).AccessToken
But is it possible to extend this command to populate my optional claim? Currently my manifest looks like this:
"optionalClaims": {
"idToken": [
{
"additionalProperties": [],
"essential": false,
"name": "onprem_sid",
"source": null
}
],
"accessToken": [],
"saml2Token": []
},
How I managed to get the proper audience in place without optional claims is that I used the application id with the tenant name:
$AzContext = Get-AzContext
$accesstoken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(
$AzContext.'Account',
$AzContext.'Environment',
$AzContext.'Tenant'.'Id',
$null,
[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never,
$null,
'https://cfbf35b2-6346-48dc-9ac2-b77e77f90933.mytenant.onmicrosoft.com'
).AccessToken
I also did put that as my application id URL and the audience on GCP configuration and managed to get proper token from GCP.
• To get optional claims in the Access token, you will have to define them in your app manifest since access tokens are always generated using the manifest of the resource, not the client. To configure optional claims,
Go to the Azure portal.
Search for and select Azure Active Directory.
Under Manage, select App registrations.
Select the application you want to configure optional claims for in the list
Then under manage, select manifest option which opens an editor and edit the access token option in it as given below as a sample for reference to get the optional claims configured in the access token. Once finished, save it and the claims will be included in the token for your application: -
The following application manifest entry adds the auth_time, ipaddr, and upn optional claims to ID, access, and SAML tokens.
‘ "optionalClaims": {
"idToken": [
{
"name": "auth_time",
"essential": false
}
],
"accessToken": [
{
"name": "ipaddr",
"essential": false
}
],
"saml2Token": [
{
"name": "upn",
"essential": false
},
{
"name": "extension_ab603c56068041afb2f6832e2a17e237_skypeId",
"source": "user",
"essential": false
}
]
} ‘
So, once you save the manifest with the above said changes and request an access token as usual according to the command stated by you, these mentioned optional claims will be included in the JWT access token.
Please refer this below link for more information: -
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims#configuring-optional-claims
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"
} ]
}
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.
I have an Azure AD app and I am trying to add custom claims to a JWT. I'm using the claims mapping feature in Azure for my specific app, and updated the app manifest in the Azure Portal to include the optional claims. However, when I log in and view the decoded access token, the claim is not present in the token. I haven't found much documentation relating to using extension attributes as claims, but from what I've found it should follow the same patterns, but it is not working as expected.
How do I add a custom claim, sourced from a custom property in the user object in AD, to a JWT when the user logs in?
Thanks in advance!
Steps to re-create
Use the Azure AD Graph API to register a directory extension
Request:
POST https://graph.windows.net/mytenant.onmicrosoft.com/applications/<application-object-id>/extensionProperties?api-version=1.5
Body:
{
"name": "customUserRoles",
"dataType": "String",
"targetObjects": ["User"]
}
Write a value to the extension for a specific AD user
Request:
PATCH https://graph.windows.net/mytenant.onmicrosoft.com/users/user123#mytenant.onmicrosoft.com?api-version=1.5
Body:
{
"extension_<appId>_customUserRoles": "My Custom Role 1, Another Role 2"
}
In PowerShell, I installed the Azure AD module: Install-Module -Name AzureADPreview
Create an Azure AD policy
New-AzureADPolicy -Definition #('{"ClaimsMappingPolicy":{"Version": 1, "IncludeBasicClaimSet": "true", "
ClaimsSchema": [ { "Source": "user", "ID": "extension_<appId>_customUserRoles", "JwtClaimType": "customUserRoles" } ] } }') -DisplayName "customUserRoles" -Type "ClaimsMappingPolicy"
Add the policy to the service principal
Add-AzureADServicePrincipalPolicy -Id <service-principla-id> -RefObjectId <azure-ad-policy-id>
In the Azure Portal, navigate to Azure AD -> App Registrations -> My App -> Manifest
Update the following properties
{
...
"acceptMappedClaims: true,
"optionalClaims": {
"idToken": [
{
"name": "extension_<appId>_customUserRoles",
"source": "user",
"essential": false,
}
],
"accessToken": [
{
"name": "extension_<appId>_customUserRoles",
"source": "user",
"essential": false,
}
],
"samlToken": []
}
}
Save the file
Navigate to https://login.microsoftonline.com/mytenant.onmicrosoft.com/oauth2/authorize?client_id=<appId>&response_type=token&resource=https://mytenant.sharepoint.com and login with Azure AD user account user123#mytenant.onmicrosoft.com
In the URL, copy the value of the access_token parameter
Navigate to https://jwt.ms and paste the access token in the text area
In the decoded token section, the custom claim customUserRoles is not present
My expectation is I should see a new claim called customUserRoles or extn.customUserRoles in the decoded token.
What steps am I missing? I haven't gotten any errors throughout this process, but it doesn't appear to be working as the documentation suggests.
Reference Material
I have read through Microsoft's documentation on these topics:
Optional Claims: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims
Claims Mapping: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-claims-mapping
I have also read through various forum posts and blog articles relating to this:
https://devonblog.com/cloud/azure-ad-adding-employeeid-claims-in-azure-ad-jwt-token/
http://www.redbaronofazure.com/?p=7566
https://social.msdn.microsoft.com/Forums/en-US/3e5114b6-24d6-4c60-b72b-b4c90baeecac/access-token-missing-optional-claims-that-are-schema-extensions-implicit-grant-flow
https://social.msdn.microsoft.com/Forums/en-US/dbeeed63-8d3f-4c27-b416-431f9fe6c729/providing-directory-extension-optional-claims-and-returning-value-within-token?forum=WindowsAzureAD
Based on this official doc :
Access tokens are always generated using the manifest of the resource,
not the client. So in the request
...scope=https://graph.microsoft.com/user.read... the resource is
Graph. Thus, the access token is created using the Graph manifest, not
the client's manifest. Changing the manifest for your application will
never cause tokens for Graph to look different. In order to validate
that your accessToken changes are in effect, request a token for your
application, not another app.
And based on your requirement , it is impossible if you want to make some change on an access token which resource is sharepoint online which is a multi-tenant app created and managed by MSFT.
For this doc , I also did some research for you . And the same , you should have control of the service side app so that you can make that happen.
This is my policy role assignment command :
$nsp = New-AzureADPolicy -Definition #('{"ClaimsMappingPolicy":{"Version":1,"IncludeBasicClaimSet":"true", "ClaimsSchema": [{"Source":"user","ID":"mailnickname","JwtClaimType":"testclaim"}]}}') -DisplayName "StanCustomCliamDemo_surname" -Type "ClaimsMappingPolicy"
Add-AzureADServicePrincipalPolicy -RefObjectId $nsp.Id -Id '<obj id of service side app>'
Token result :
What's more , pls note that extension_<appId>_customUserRoles is not a valid user source ID . For all valid user source ID , pls refer to here .
Hope it helps .
I'm trying to overload the OAuth ID Token by adding additional claims. I can already use the Graph API to get the claims that I need but I would like to understand if it's possible to add the additional claims directly into the ID Token? I've updated the manifest by adding the required claims in and then flipping "acceptMappedClaims" to true, however I still don't see these in the ID token. What am I missing?
"optionalClaims": {
"idToken": [
{
"name": "employeeid",
"source": "user",
"essential": true,
"additionalProperties": []
},
{
"name": "mail",
"source": "user",
"essential": true,
"additionalProperties": []
}
],
"accessToken": [],
"saml2Token": []
},
"acceptMappedClaims": true,
This depends on where the ID token is generated from. If it's on-premises AD and federated identity is used take a look at Customizing the OIDC id_token in ADFS 2016.
If it's just a cloud identity I'd take a look at the second link jwmiller5 posted or this one: how-to-set-claims-from-asp-net-openid-connect-owin-components.
Hope this helps,
Bernie
If you are trying to add additional claims into your AD token, you would need Azure AD premium and you can add the values as attributes. See Claim augmentation with Azure AD authentcation
If you just need the claims in one particular application, you can add the claims in the app itself. See Azure AD PostAuthentication add claims
Using the extension API documented here:
https://msdn.microsoft.com/en-us/library/azure/ad/graph/howto/azure-ad-graph-api-directory-schema-extensions
in conjuction with the B2C Graph Client sample:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-devquickstarts-graph-dotnet
I created a custom attribute via the AD Graph API for directory schema extensions using this API:
POST
https://graph.windows.net/contoso.onmicrosoft.com/applications/<applicationObjectId>/extensionProperties?api-version=1.6
{
name: "OrgRoleId",
dataType: "String",
targetObjects: [
"User"
]
}
(Note I changed the API version to 1.6).
The API created custom attributes appear using the B2CGraphClient sample and has the same data as those registered via the Azure portal for B2C.
However, these API created custom attributes don't appear in the Azure portal 'User attributes' blade for the tenant, while those custom attributes created via the Azure portal for the B2C tenant do.
Note that I can successfully read and write these extension values for users (via the Graph API). I just cannot put them into claims because they don't appear on the 'User attributes' blade nor the policy claims blade in the Azure portal, and therefore they are not added as claims to the token.
What I am missing/doing wrong?
Output from B2C.exe Get-extension-attribute <b2c-extensions-app objectId>. *_Test1 appears (portal created), while *_UserRoleId does not (API created):
{
"odata.metadata": "https://graph.windows.net/<tenant_id>/$metadata#directoryObjects/Microsoft.DirectoryServices.ExtensionProperty",
"value": [
{
"odata.type": "Microsoft.DirectoryServices.ExtensionProperty",
"objectType": "ExtensionProperty",
"objectId": "f58bc813-632c-486b-bff1-61695eeab691",
"deletionTimestamp": null,
"appDisplayName": "",
"name": "extension_<object_id>_Test1",
"dataType": "String",
"isSyncedFromOnPremises": false,
"targetObjects": [
"User"
]
},
{
"odata.type": "Microsoft.DirectoryServices.ExtensionProperty",
"objectType": "ExtensionProperty",
"objectId": "5e69b2d9-1ab0-463f-a231-5c188e92b4a1",
"deletionTimestamp": null,
"appDisplayName": "",
"name": "extension_<object_id>_UserRoleId",
"dataType": "String",
"isSyncedFromOnPremises": false,
"targetObjects": [
"User"
]
}
...
When you add an extension attribute through the portal, it is created in the directory and owned by the b2c-extensions-app application and it is also added to a tenant-wide policy. That is what allows you to use them in application policies as you create them.
When you create an extension attribute using Graph API, it is not added to the policy and usually created on an application other than b2c-extensions-app. You can use these properties directly in custom policies, but they will not appear in the portal and cannot be used in the policies created through the portal.
It is a best practice to just create the extension properties through the portal so they are available for all policies. This allows customers to mix and match custom policies with built-in b2c user flows.