Working with custom/extension attributes in Entra External ID (CIAM)
This is a utility post, as I will post more about extension attributes, but it can live on its own.
The scenario is that I want to have a web. app calling an API and adding the extension attribute to the tokens.
The sample is here.
Extension attributes are where you want to add a new attribute to the schema.
A common one is "EmployeeID"
Here is the PowerShell 7 script to create an extension attribute.
Note that we are linking the extension attribute to the API application using the application's objectID.
# Define your Azure AD tenant ID, client ID, and client secret
$tenantId = "tenant.onmicrosoft.com"
$clientId = "cf9...bfc"
$clientSecret = "6~_...ae."
# Define the resource and scope
$scope = "https://graph.microsoft.com/.default"
# Define the URL for obtaining the access token
$url = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
# Define the body for the API request
$body = @{
"client_id" = $clientId
"scope" = $scope
"client_secret" = $clientSecret
"grant_type" = "client_credentials"
}
# Send the API request and store the response
$response = Invoke-RestMethod -Uri $url -Method Post -Body $body
# Extract the access token from the response
$accessToken = $response.access_token
# Display the access token
# Write-Output "Access Token: $accessToken"
# Define the application object ID
$appId = "850...84d"
# Define the Graph API URL for extensionProperties
$url = "https://graph.microsoft.com/v1.0/applications/$appId/extensionProperties"
# Define the headers for the API request
$headers = @{
"Authorization" = "Bearer $accessToken"
"Content-Type" = "application/json"
}
# Define the body for the API request
$body = @{
"name" = "EmployeeID"
"dataType" = "String"
"targetObjects" = @("User")
} | ConvertTo-Json
# Send the API request and store the response
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Post -Body $body
# Display the response
$response
This uses client credentials, so you need to create a separate app. registration with a secret to support the call.
Now, let's display the extension attributes attached to the application.
Here is the PowerShell 7 script.
# Define your Entra External ID tenant ID, client ID, and client secret
$tenantId = "tenant.onmicrosoft.com"
$clientId = "cf9...bfc"
$clientSecret = "6~_...ae."
# Define the resource and scope
$scope = "https://graph.microsoft.com/.default"
# Define the URL for obtaining the access token
$url = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
# Define the body for the API request
$body = @{
"client_id" = $clientId
"scope" = $scope
"client_secret" = $clientSecret
"grant_type" = "client_credentials"
}
# Send the API request and store the response
$response = Invoke-RestMethod -Uri $url -Method Post -Body $body
# Extract the access token from the response
$accessToken = $response.access_token
# Display the access token
# Write-Output "Access Token: $accessToken"
# Define the application objectID
$appId = "850...84d"
# Define the Graph API URL for extensionProperties
$url = "https://graph.microsoft.com/v1.0/applications/$appId/extensionProperties"
# Define the headers for the API request
$headers = @{
"Authorization" = "Bearer $accessToken"
"Content-Type" = "application/json"
}
# Send the API request and store the response
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Get
# Display the extensionProperties
$response.value | Format-Table -Property Name, DataType, TargetObjects
This results in:
name dataType targetObjects
---- -------- -------------
extension_276...f43_EmployeeID String {User}
Now, let's link this attribute to a user.
Here is the PowerShell 7 script.
# Define your Entra External ID tenant ID, client ID, and client secret
$tenantId = "tenant.onmicrosoft.com"
$clientId = "cf9...bfc"
$clientSecret = "6~_...ae."
# Define the resource and scope
$scope = "https://graph.microsoft.com/.default"
# Define the URL for obtaining the access token
$url = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
# Define the body for the API request
$body = @{
"client_id" = $clientId
"scope" = $scope
"client_secret" = $clientSecret
"grant_type" = "client_credentials"
}
# Send the API request and store the response
$response = Invoke-RestMethod -Uri $url -Method Post -Body $body
# Extract the access token from the response
$accessToken = $response.access_token
# Display the access token
# Write-Output "Access Token: $accessToken"
# Define the user's UPN
$upn = "b1c...7e3@tenant.onmicrosoft.com"
# Define the Graph API URL for the user
$url = "https://graph.microsoft.com/v1.0/users/$upn"
# Define the headers for the API request
$headers = @{
"Authorization" = "Bearer $accessToken"
"Content-Type" = "application/json"
}
# Define the body for the API request
$body = @{
"extension_276...f43_EmployeeID" = "098765"
} | ConvertTo-Json
# Send the API request and store the response
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Patch -Body $body
# Display the response
$response
Unfortunately, I cannot find a way to display the user attributes that include the extension attribute.
Here is the PowerShell 7 script to display some of the user attributes.
# Define your Entra External ID tenant ID, client ID, and client secret
$tenantId = "tenant.onmicrosoft.com"
$clientId = "cf9...bfc"
$clientSecret = "6~_...ae."
# Define the resource and scope
$scope = "https://graph.microsoft.com/.default"
# Define the URL for obtaining the access token
$url = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
# Define the body for the API request
$body = @{
"client_id" = $clientId
"scope" = $scope
"client_secret" = $clientSecret
"grant_type" = "client_credentials"
}
# Send the API request and store the response
$response = Invoke-RestMethod -Uri $url -Method Post -Body $body
# Extract the access token from the response
$accessToken = $response.access_token
# Display the access token
# Write-Output "Access Token: $accessToken"
# Define the user's UPN
$upn = "b1c...7e3@tenant.onmicrosoft.com"
# Define the Graph API URL for the user
$url = "https://graph.microsoft.com/v1.0/users/$upn"
# Define the headers for the API request
$headers = @{
"Authorization" = "Bearer $accessToken"
"Content-Type" = "application/json"
}
# Send the API request and store the response
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Get
# Display the user's attributes
$response | ConvertTo-Json -Depth 4
This results in:
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"businessPhones": [],
"displayName": "Easy Auth",
"givenName": "Easy",
"jobTitle": null,
"mail": null,
"mobilePhone": null,
"officeLocation": null,
"preferredLanguage": null,
"surname": "Auth",
"userPrincipalName": "b1c...7e3@tenant.onmicrosoft.com",
"id": "b1c...7e3"
}
I also have a utility that does this via the old AzureAD API commands.
Using that utility, I can display the extension attributes for the user, e.g.:
"externalUserConvertedOn": null,
"externalUserState": null,
"externalUserStateChangeDateTime": null,
"userType": "Member",
"extension_276...f43_EmployeeID": "098765",
"extension_a3d...8c4_Custom": "123456",
"employeeOrgData": null,
"passwordProfile": null,
"assignedLicenses": [],
This is where it gets confusing 😢
The “Custom” extension attribute was created using the old B2C method and the utility above, which involved using the clientID of the B2C extension application.
Display name b2c-extensions-app. Do not modify. Used by AADB2C for storing
user data.
Application (client) ID
a3d...8c4
However, the “EmployeeID” extension attribute was created using the PowerShell above, which involved using the API application's clientID.
ToDo API 276...f43
All good!