Microsoft Entra ID
Overview
Section titled “Overview”The EQTY Governance Platform integrates with Microsoft Entra ID (formerly Azure Active Directory) for enterprise-grade authentication and authorization.
Required app registrations:
- Governance Platform Backend API – Web application for backend API authentication and token validation
- Microsoft Graph API Access – Separate app registration for user and organization management via Microsoft Graph
- Governance Worker – M2M confidential client for automated governance workflow execution
- Governance Studio SPA – Single Page Application for frontend authentication
This guide covers:
- Creating app registrations in Entra ID (Azure CLI or Portal)
- Configuring API permissions and scopes
- Setting up Microsoft Graph access
- Post-installation organization and admin user setup
- Configuring the Helm chart for Entra ID
Prerequisites
Section titled “Prerequisites”- Microsoft Entra ID tenant (Azure AD tenant)
- Azure CLI installed and configured (for CLI method)
- Permissions to create app registrations in the tenant
- Tenant ID (found in Azure Portal > Microsoft Entra ID > Overview)
Helm Configuration
Section titled “Helm Configuration”Add the following to the values.yaml and secrets.yaml files. Placeholders will be filled in throughout the steps below.
values.yaml:
auth-service: config: idp: provider: "entra" issuer: "https://login.microsoftonline.com/<tenant-id>/v2.0" # Filled in after step 1 entra: tenantId: "<tenant-id>" # Filled in after step 1 defaultRoles: "user"
governance-service: config: authProvider: "entra" entraTenantId: "<tenant-id>" # Filled in after step 1
governance-studio: config: authProvider: "entra" entraTenantId: "<tenant-id>" # Filled in after step 1 entraClientId: "<spa-client-id>" # Filled in after step 5 entraScopes: "openid profile email offline_access api://<backend-client-id>/access_as_user" # Filled in after steps 2 & 5secrets.yaml:
global: secrets: create: true auth: provider: "entra" entra: secretName: "platform-entra" values: clientId: "<backend-client-id>" # Filled in after step 2 clientSecret: "<backend-client-secret>" # Filled in after step 2 tenantId: "<tenant-id>" # Filled in after step 1 graphClientId: "<graph-client-id>" # Filled in after step 3 graphClientSecret: "<graph-client-secret>" # Filled in after step 3 governanceWorker: secretName: "platform-governance-worker" values: encryptionKey: "<worker-encryption-key>" # Generate: openssl rand -base64 32 clientId: "<worker-client-id>" # Filled in after step 4 clientSecret: "<worker-client-secret>" # Filled in after step 4Quick Start (CLI Method - Recommended)
Section titled “Quick Start (CLI Method - Recommended)”1. Authenticate to Azure
Section titled “1. Authenticate to Azure”The tenant ID can be found in Azure Portal > Microsoft Entra ID > Overview. Login to Azure with the tenant:
az login --tenant <tenant-id>Verify the correct tenant:
az account showUpdate values.yaml with the tenant ID.
values.yaml -> auth-service.config.idp:
issuer: "https://login.microsoftonline.com/<tenant-id>/v2.0"entra: tenantId: "<tenant-id>"values.yaml -> governance-service.config:
entraTenantId: "<tenant-id>"values.yaml -> governance-studio.config:
entraTenantId: "<tenant-id>"Update secrets.yaml with the tenant ID.
secrets.yaml -> global.secrets.auth.entra.values:
tenantId: "<tenant-id>"2. Create Backend API App Registration
Section titled “2. Create Backend API App Registration”Create the backend API app registration. Replace <domain> with the governance platform domain (e.g. governance.example.com):
az ad app create \ --display-name "EQTY Governance Platform Backend" \ --sign-in-audience AzureADMyOrg \ --enable-id-token-issuance true \ --enable-access-token-issuance true \ --web-redirect-uris "https://<domain>/callback"Save the appId and id from the output:
appIdis the Client IDidis the Object ID
Store both as variables for use in subsequent commands:
BACKEND_CLIENT_ID="<app-id-from-output>"BACKEND_OBJECT_ID="<id-from-output>"Create a service principal and set the application ID URI:
az ad sp create --id $BACKEND_CLIENT_ID
az ad app update --id $BACKEND_CLIENT_ID \ --identifier-uris "api://$BACKEND_CLIENT_ID"Generate a unique ID for the API scope:
uuidgenSave this UUID — it will be used as SCOPE_ID in the next step.
Create a file named backend-scope.json with the following content. Replace SCOPE_ID with the UUID from above:
{ "api": { "requestedAccessTokenVersion": 2, "oauth2PermissionScopes": [ { "id": "SCOPE_ID", "adminConsentDescription": "Allow the application to access EQTY Governance Platform on behalf of the signed-in user", "adminConsentDisplayName": "Access EQTY Governance Platform", "isEnabled": true, "type": "User", "userConsentDescription": "Allow the application to access EQTY Governance Platform on your behalf", "userConsentDisplayName": "Access EQTY Governance Platform", "value": "access_as_user" } ] }}Add the API scope to the app registration:
az rest --method PATCH \ --uri "https://graph.microsoft.com/v1.0/applications/$BACKEND_OBJECT_ID" \ --headers "Content-Type=application/json" \ --body @backend-scope.jsonCreate a client secret for the backend app:
az ad app credential reset \ --id $BACKEND_CLIENT_ID \ --append \ --query password \ --output tsvSave the client secret — it cannot be retrieved later.
Update secrets.yaml with the backend credentials.
secrets.yaml -> global.secrets.auth.entra.values:
clientId: "<backend-client-id>"clientSecret: "<client-secret-from-output>"Update values.yaml with the backend client ID (used in the scopes string).
values.yaml -> governance-studio.config:
entraScopes: "openid profile email offline_access api://<backend-client-id>/access_as_user"3. Create Graph API Access App Registration
Section titled “3. Create Graph API Access App Registration”Create the Graph API access app registration:
az ad app create \ --display-name "EQTY Governance Platform Graph API" \ --sign-in-audience AzureADMyOrgSave the appId from the output — this is the Graph API Client ID.
Store it as a variable for use in subsequent commands:
GRAPH_CLIENT_ID="<app-id-from-output>"Create a service principal for the app:
az ad sp create --id $GRAPH_CLIENT_IDAdd Microsoft Graph API permissions for reading users and organization information:
az ad app permission add \ --id $GRAPH_CLIENT_ID \ --api 00000003-0000-0000-c000-000000000000 \ --api-permissions df021288-bdef-4463-88db-98f22de89214=Role 498476ce-e0fe-48b0-b801-37ba7e2685c6=RoleThis adds:
User.Read.All(df021288-bdef-4463-88db-98f22de89214) – Read all users’ full profilesOrganization.Read.All(498476ce-e0fe-48b0-b801-37ba7e2685c6) – Read organization information
Grant admin consent for the API permissions (requires admin privileges):
az ad app permission admin-consent --id $GRAPH_CLIENT_IDCreate a client secret for the Graph API app:
az ad app credential reset \ --id $GRAPH_CLIENT_ID \ --append \ --query password \ --output tsvSave the client secret — it cannot be retrieved later.
Update secrets.yaml with the Graph API credentials.
secrets.yaml -> global.secrets.auth.entra.values:
graphClientId: "<graph-client-id>"graphClientSecret: "<client-secret-from-output>"4. Create Governance Worker App Registration
Section titled “4. Create Governance Worker App Registration”The Governance Worker is a confidential M2M (machine-to-machine) client used for automated governance workflow execution.
Create the Governance Worker app registration:
az ad app create \ --display-name "EQTY Governance Worker" \ --sign-in-audience AzureADMyOrgSave the appId from the output — this is the Worker Client ID.
Store it as a variable for use in subsequent commands:
WORKER_CLIENT_ID="<app-id-from-output>"Create a service principal for the app:
az ad sp create --id $WORKER_CLIENT_IDCreate a client secret for the Worker app:
az ad app credential reset \ --id $WORKER_CLIENT_ID \ --append \ --query password \ --output tsvSave the client secret — it cannot be retrieved later.
Generate an encryption key for the worker:
openssl rand -base64 32Update secrets.yaml with the worker credentials.
secrets.yaml -> global.secrets.governanceWorker.values:
encryptionKey: "<generated-key>"clientId: "<worker-client-id>"clientSecret: "<client-secret-from-output>"5. Create Frontend SPA App Registration
Section titled “5. Create Frontend SPA App Registration”Create the SPA app registration:
az ad app create \ --display-name "EQTY Governance Studio" \ --sign-in-audience AzureADMyOrg \ --enable-id-token-issuance true \ --enable-access-token-issuance trueSave the appId and id from the output:
appIdis the SPA Client IDidis the SPA Object ID
Store both as variables:
SPA_CLIENT_ID="<app-id-from-output>"SPA_OBJECT_ID="<id-from-output>"Configure SPA redirect URIs and set token version to v2.0. Replace <domain> with the governance platform domain:
az rest --method PATCH \ --uri "https://graph.microsoft.com/v1.0/applications/$SPA_OBJECT_ID" \ --headers "Content-Type=application/json" \ --body "{\"spa\":{\"redirectUris\":[\"https://<domain>\"]},\"api\":{\"requestedAccessTokenVersion\":2}}"Create a service principal for the SPA app:
az ad sp create --id $SPA_CLIENT_IDGet the backend API scope ID:
az ad app show --id $BACKEND_CLIENT_ID --query 'api.oauth2PermissionScopes[0].id' -o tsvSave this scope ID — it will be used in the next command.
Grant the SPA access to the backend API. Replace <scope-id> with the value from above:
az ad app permission add \ --id $SPA_CLIENT_ID \ --api $BACKEND_CLIENT_ID \ --api-permissions <scope-id>=ScopeOptionally, grant admin consent for the API permission:
az ad app permission admin-consent --id $SPA_CLIENT_IDNote: Admin consent may require higher privileges. If this fails, grant consent later through the Azure Portal.
Update values.yaml with the SPA client ID.
values.yaml -> governance-studio.config:
entraClientId: "<spa-client-id>"Alternative: Web UI Setup
Section titled “Alternative: Web UI Setup”Click to expand Azure Portal instructions
Using the Azure Portal
Section titled “Using the Azure Portal”1. Create Backend API App Registration
Section titled “1. Create Backend API App Registration”- Navigate to Azure Portal > Microsoft Entra ID > App registrations
- Click “New registration”
- Name: “EQTY Governance Platform Backend”
- Supported account types: “Accounts in this organizational directory only”
- Redirect URI: Web,
https://<domain>/callback - Click “Register”
- Note the Application (client) ID and Directory (tenant) ID
- Navigate to “Manifest”
- Find “accessTokenAcceptedVersion” and set it to
2(for v2.0 tokens) - Click “Save”
- Navigate to “Expose an API”
- Set Application ID URI:
api://<client-id> - Add a scope:
- Scope name:
access_as_user - Admin consent display name: “Access EQTY Governance Platform”
- Admin consent description: “Allow the application to access EQTY Governance Platform on behalf of the signed-in user”
- State: Enabled
- Click “Add scope”
- Scope name:
- Navigate to “Certificates & secrets”
- Click “New client secret”
- Description: “Governance Platform Secret”
- Expires: 24 months (or the organization’s policy)
- Click “Add”
- Copy the secret Value immediately — it won’t be shown again
Update values.yaml with the tenant ID and backend credentials.
values.yaml -> auth-service.config.idp:
issuer: "https://login.microsoftonline.com/<tenant-id>/v2.0"entra: tenantId: "<tenant-id>"values.yaml -> governance-service.config:
entraTenantId: "<tenant-id>"values.yaml -> governance-studio.config:
entraTenantId: "<tenant-id>"entraScopes: "openid profile email offline_access api://<backend-client-id>/access_as_user"Update secrets.yaml with the backend credentials.
secrets.yaml -> global.secrets.auth.entra.values:
clientId: "<backend-client-id>"clientSecret: "<client-secret>"tenantId: "<tenant-id>"2. Create Graph API Access App Registration
Section titled “2. Create Graph API Access App Registration”- Create another app registration: “EQTY Governance Platform Graph API”
- Navigate to “API permissions”
- Click “Add a permission”
- Select “Microsoft Graph” > “Application permissions”
- Add:
User.Read.All,Organization.Read.All - Click “Add permissions”
- Click “Grant admin consent” (requires admin role)
- Navigate to “Certificates & secrets”
- Create a client secret
- Save the secret value
Update secrets.yaml with the Graph API credentials.
secrets.yaml -> global.secrets.auth.entra.values:
graphClientId: "<graph-client-id>"graphClientSecret: "<graph-client-secret>"3. Create Governance Worker App Registration
Section titled “3. Create Governance Worker App Registration”- Create app registration: “EQTY Governance Worker”
- Supported account types: “Accounts in this organizational directory only”
- No redirect URI needed (M2M client)
- Click “Register”
- Navigate to “Certificates & secrets”
- Create a client secret
- Save the secret value
Generate an encryption key for the worker:
openssl rand -base64 32Update secrets.yaml with the worker credentials.
secrets.yaml -> global.secrets.governanceWorker.values:
encryptionKey: "<generated-key>"clientId: "<worker-client-id>"clientSecret: "<worker-client-secret>"4. Create SPA App Registration
Section titled “4. Create SPA App Registration”- Create app registration: “EQTY Governance Studio”
- Navigate to “Manifest”
- Find “accessTokenAcceptedVersion” and set it to
2(for v2.0 tokens) - Click “Save”
- Navigate to “Authentication”
- Click “Add a platform” > “Single-page application”
- Redirect URIs:
https://<domain> - Check “ID tokens” under Implicit grant and hybrid flows
- Check “Access tokens” under Implicit grant and hybrid flows
- Click “Configure”
- Navigate to “API permissions”
- Click “Add a permission”
- Select “My APIs”
- Select “EQTY Governance Platform Backend”
- Check the
access_as_userscope - Click “Add permissions”
- Optional: Click “Grant admin consent”
Update values.yaml with the SPA client ID.
values.yaml -> governance-studio.config:
entraClientId: "<spa-client-id>"Post-Installation: Create Organization and Admin User
Section titled “Post-Installation: Create Organization and Admin User”After deploying the platform, create an organization and admin user:
# Add to values.yamlentra: createOrganization: true organizationName: "your-org-name" displayName: "Organization Display Name"
createPlatformAdmin: true platformAdminEmail: "admin@example.onmicrosoft.com" # Must exist in Entra tenantId: "<tenant-id>"This will:
- Create an organization in the governance database
- Look up the Entra user by email via Graph API
- Create a platform-admin user record linked to the Entra user
Verification
Section titled “Verification”Verify the OIDC discovery endpoint:
curl "https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration"Test token issuance with backend API credentials:
curl -X POST "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "client_id=<backend-client-id>" \ -d "client_secret=<backend-client-secret>" \ -d "scope=api://<backend-client-id>/.default" \ -d "grant_type=client_credentials"Test Graph API access:
curl -X POST "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "client_id=<graph-client-id>" \ -d "client_secret=<graph-client-secret>" \ -d "scope=https://graph.microsoft.com/.default" \ -d "grant_type=client_credentials"If both commands succeed and return access tokens, the Entra configuration is correct.
Security Best Practices
Section titled “Security Best Practices”- Use Certificate Authentication: For production, consider using certificates instead of client secrets
- Rotate Secrets: Regularly rotate client secrets (recommended: every 6-12 months)
- Conditional Access: Implement Conditional Access policies for enhanced security
- Multi-Factor Authentication: Require MFA for all users
- Least Privilege: Only grant the minimum required Graph API permissions
- Monitor Sign-ins: Enable sign-in logs and monitor for suspicious activity
- App Consent Policies: Configure app consent policies to control which apps users can consent to
Troubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”- “AADSTS65001: The user or administrator has not consented to use the application” — Solution: Grant admin consent for API permissions in the Azure Portal
- “AADSTS700016: Application not found in the directory” — Solution: Verify the client ID is correct and the app registration exists in the tenant
- “Invalid scope” — Solution: Ensure the
entraScopesconfiguration includesapi://<backend-client-id>/access_as_user - Graph API returns 403 Forbidden — Solution: Verify admin consent was granted for Graph API permissions