Microsoft Entra ID Setup Guide
This guide covers the complete setup of Microsoft Entra ID as an identity provider for the Custody Engine.
Prerequisites
- Access to Microsoft Azure Portal with permissions to create App Registrations
- Azure AD PowerShell module or Microsoft Graph PowerShell SDK installed
- Tenant ID of your Azure AD directory
1. Register the Application
1.1 Create App Registration
- Navigate to Azure Portal → Microsoft Entra ID → App registrations
- Click New registration
- Configure the following:
- Name:
Custody Desktop(or your preferred name) - Supported account types: Select based on your requirements
- Redirect URI:
- Platform: Public client/native (mobile & desktop)
- URI:
ecs://oauth-response
- Name:
- Click Register
1.2 Note Required Values
After registration, note the following values from the Overview page:
| Value | Location | Example |
|---|---|---|
| Application (client) ID | Overview | 12345678-1234-1234-1234-123456789abc |
| Directory (tenant) ID | Overview | 87654321-4321-4321-4321-cba987654321 |
| Object ID | Overview | abcdef12-3456-7890-abcd-ef1234567890 |
2. Configure Authentication
2.1 Platform Configuration
- Go to Authentication in your app registration
- Under Platform configurations, verify or add:
- Platform: Mobile and desktop applications
- Custom redirect URI:
ecs://oauth-response
- Under Advanced settings:
- Enable Allow public client flows → Set to Yes
- Click Save
2.2 Enable PKCE
The application uses PKCE (Proof Key for Code Exchange) for secure authorization. This is automatically supported when using the public client flow with custom redirect URIs.
3. Configure API Permissions
- Go to API permissions
- Click Add a permission → Microsoft Graph → Delegated permissions
- Add the following permissions:
openid(required for OIDC)profile(for name claims)email(for email claim)
- Click Add permissions
- If required by your organization, click Grant admin consent for [Organization]
4. Configure Claims
The application requires both standard and custom claims in the JWT tokens.
4.1 Optional Claims (Standard Claims)
- Go to Token configuration
- Click Add optional claim
- Select ID token type and add:
emailgiven_namefamily_name
- Repeat for Access token type with the same claims
- Click Add
Alternatively, edit the Manifest directly and add:
"optionalClaims": {
"idToken": [
{
"name": "email",
"source": null,
"essential": true,
"additionalProperties": []
},
{
"name": "given_name",
"source": null,
"essential": false,
"additionalProperties": []
},
{
"name": "family_name",
"source": null,
"essential": false,
"additionalProperties": []
}
],
"accessToken": [
{
"name": "email",
"source": null,
"essential": true,
"additionalProperties": []
},
{
"name": "given_name",
"source": null,
"essential": false,
"additionalProperties": []
},
{
"name": "family_name",
"source": null,
"essential": false,
"additionalProperties": []
}
],
"saml2Token": []
}4.2 Custom Claims (Server Keys)
The application requires custom claims for server public keys:
srv_enc_pub- Server RSA public key for encryptionsrv_sig_pub- Server ECDSA public key for signatures
4.2.1 Create Extension Attributes
Using Azure AD PowerShell:
# Connect to Azure AD
Connect-AzureAD
# Get your application object ID
$appObjectId = "YOUR_APP_OBJECT_ID"
# Create srv_enc_pub extension attribute
New-AzureADApplicationExtensionProperty `
-ObjectId $appObjectId `
-Name "srv_enc_pub" `
-DataType "String" `
-TargetObjects "User"
# Create srv_sig_pub extension attribute
New-AzureADApplicationExtensionProperty `
-ObjectId $appObjectId `
-Name "srv_sig_pub" `
-DataType "String" `
-TargetObjects "User"Using Microsoft Graph PowerShell SDK:
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Application.ReadWrite.All"
# Create extension properties
$params = @{
name = "srv_enc_pub"
dataType = "String"
targetObjects = @("User")
}
New-MgApplicationExtensionProperty -ApplicationId $appObjectId -BodyParameter $params
$params = @{
name = "srv_sig_pub"
dataType = "String"
targetObjects = @("User")
}
New-MgApplicationExtensionProperty -ApplicationId $appObjectId -BodyParameter $params4.2.2 Create Claims Mapping Policy
# Replace {app-id-no-dashes} with your Application ID without dashes
# Example: 12345678-1234-1234-1234-123456789abc becomes 1234567812341234123456789abc
$policyDefinition = @"
{
"ClaimsMappingPolicy": {
"Version": 1,
"IncludeBasicClaimSet": "true",
"ClaimsSchema": [
{
"Source": "user",
"ExtensionID": "extension_{app-id-no-dashes}_srv_enc_pub",
"JwtClaimType": "srv_enc_pub"
},
{
"Source": "user",
"ExtensionID": "extension_{app-id-no-dashes}_srv_sig_pub",
"JwtClaimType": "srv_sig_pub"
}
]
}
}
"@
# Create the policy
$policy = New-AzureADPolicy `
-Definition $policyDefinition `
-DisplayName "CustodyClaimsPolicy" `
-Type "ClaimsMappingPolicy"
# Get the service principal for your application
$servicePrincipal = Get-AzureADServicePrincipal -Filter "AppId eq 'YOUR_APPLICATION_CLIENT_ID'"
# Assign the policy to the service principal
Add-AzureADServicePrincipalPolicy `
-Id $servicePrincipal.ObjectId `
-RefObjectId $policy.Id4.2.3 Enable Claims Mapping in Manifest
- Go to Manifest in your app registration
- Set
acceptMappedClaimstotrue:
"acceptMappedClaims": true- Click Save
Note: If you receive an error about
acceptMappedClaims, you may need to configure a custom signing key. See Microsoft Documentation for details.
4.3 Set User Extension Attribute Values
For each user that will authenticate, set the server key values:
# Get the user
$user = Get-AzureADUser -ObjectId "user@domain.com"
# Set extension attribute values
# Replace {app-id-no-dashes} with your Application ID without dashes
Set-AzureADUserExtension -ObjectId $user.ObjectId -ExtensionName "extension_{app-id-no-dashes}_srv_enc_pub" -ExtensionValue "YOUR_RSA_PUBLIC_KEY"
Set-AzureADUserExtension -ObjectId $user.ObjectId -ExtensionName "extension_{app-id-no-dashes}_srv_sig_pub" -ExtensionValue "YOUR_ECDSA_PUBLIC_KEY"5. Application Configuration
For the application, we require this data:
issuer = "https://login.microsoftonline.com/{tenant-id}/v2.0"
audience = "{client-id}"
jwks_url = "https://login.microsoftonline.com/{tenant-id}/discovery/v2.0/keys"Replace:
{tenant-id}with your Directory (tenant) ID{client-id}with your Application (client) ID
6. Entra ID Endpoints Reference
| Purpose | URL |
|---|---|
| Authorization | https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize |
| Token | https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token |
| OIDC Discovery | https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration |
| JWKS | https://login.microsoftonline.com/{tenant-id}/discovery/v2.0/keys |
| Logout | https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/logout |
7. Verification (optional)
7.1 Test Token Acquisition
Use the OAuth 2.0 authorization code flow with PKCE to obtain a token:
- Generate a code verifier and code challenge
- Direct user to authorization endpoint with:
client_idredirect_uri=ecs://oauth-responsescope=openid profile emailresponse_type=codecode_challengeandcode_challenge_method=S256
- Exchange the authorization code for tokens at the token endpoint
7.2 Verify Token Claims
Decode the ID token (using jwt.ms or similar) and verify it contains:
- Standard claims:
email,given_name,family_name - Custom claims:
srv_enc_pub,srv_sig_pub - Correct
iss(issuer) andaud(audience) values
8. Troubleshooting
Common Issues
| Issue | Solution |
|---|---|
AADSTS50011: Reply URL mismatch | Verify ecs://oauth-response is configured as a redirect URI |
AADSTS7000218: Request body must contain client_assertion | Enable "Allow public client flows" in Authentication settings |
| Custom claims not appearing | Verify claims mapping policy is assigned to service principal |
acceptMappedClaims error | Configure a custom signing key or use a verified domain |
| Extension attributes not found | Ensure extension properties are created on the correct application |
Useful Commands
# List extension properties for an application
Get-AzureADApplicationExtensionProperty -ObjectId $appObjectId
# List claims mapping policies
Get-AzureADPolicy -All $true | Where-Object { $_.Type -eq "ClaimsMappingPolicy" }
# List policies assigned to a service principal
Get-AzureADServicePrincipalPolicy -Id $servicePrincipal.ObjectId
# Remove a claims mapping policy from service principal
Remove-AzureADServicePrincipalPolicy -Id $servicePrincipal.ObjectId -PolicyId $policy.IdPolicy Configuration
Policies are the heart of the Custody Engine's governance system. They define rules that control when any action requires approval, who can approve it, and under what conditions actions should be automatically allowed or denied.
Message Spec
Auto-generated CDDL reference for Custody Engine message payloads.