Provider API Authorization Process
This document describes the authorization process for external providers integrating with the Kontrolle+ Provider API. It reflects the current Data Service implementation for machine-to-machine authorization only.
1. Authorization Architecture Overview
Provider API authorization uses OAuth 2.0 client credentials with Microsoft Entra-issued JWT access tokens.
Flow:
- Provider backend (confidential client) requests a token from Microsoft Entra.
- Entra issues an access token for the Provider API resource.
- Provider calls the Data Service Provider API with
Authorization: Bearer <access_token>. - Data Service validates issuer, audience, token lifetime, required role, and optional client allow-list.
- Request is accepted or rejected.
Protected endpoints:
POST /api/provider/v1/controls
Auth model details:
- Authentication scheme:
ProviderJwt - Authorization policy:
ProviderApiPolicy - Required app role:
ProviderApi.Access
2. Provider Onboarding Checklist
Use one confidential client application per provider.
A. Internal platform setup
- Confirm the Provider API app registration and exposed app role
ProviderApi.Access. - Confirm runtime auth settings in target environment:
ProviderJwt:AuthorityProviderJwt:AudienceProviderJwt:AllowedClientIds(if enabled)
- Create a dedicated Entra confidential client app registration for the provider.
- Assign Provider API application permission/app role
ProviderApi.Accessto that client app. - Create provider credential:
- Preferred: certificate
- Acceptable for lower environments: client secret
- If allow-list enforcement is enabled, add provider client ID to
ProviderJwt:AllowedClientIds.
B. Provider hand-off package
Provide the following integration values:
- Environment name
- API base URL
- Authority
- Token endpoint
- Client ID
- Credential delivery mechanism (secure channel)
- Token scope (
<provider-api-app-id-uri>/.default) - Expected runtime audience (
aud) - Required role claim (
ProviderApi.Access)
C. Validation before go-live
- Acquire a token with client credentials.
- Verify token contains expected
audand role claim. - Verify provider client identity claim (
azp/appid/client_id) is allow-listed when enabled. - Execute smoke calls:
POST /api/provider/v1/controlsreturns201or200duplicate.
3. Token Acquisition Example (Client Credentials)
POST /<tenant>/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id=<provider-client-id>
client_secret=<provider-client-secret>
grant_type=client_credentials
scope=<provider-api-app-id-uri>/.default
Notes:
- Use
grant_type=client_credentials. - Do not use delegated/user flows.
- Runtime validation is performed against emitted token claims (
iss,aud,roles, and optionally client identity claim).
4. Provider API Call Example
curl -X POST "https://<data-service-host>/api/provider/v1/controls" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access_token>" \
-H "X-Provider-Id: <provider-name-optional>" \
-d @provider-payload.json
Header rules:
- Required:
Authorization: Bearer <access_token> - Optional:
X-Provider-Id(tracing only, never used for authentication)
5. Token Validation Matrix
| Validation Item | Token/Header Source | Runtime Check | Outcome if Fails |
|---|---|---|---|
| Issuer | iss claim | Must match ProviderJwt:Authority | 401 Unauthorized |
| Audience | aud claim | Must match ProviderJwt:Audience | 401 Unauthorized |
| Expiration | exp claim | Token must be valid (not expired / valid lifetime) | 401 Unauthorized |
| Required role | roles claim | Must include ProviderApi.Access | 403 Forbidden |
| Allowed client IDs (optional) | azp or appid or client_id | Must be in ProviderJwt:AllowedClientIds when configured | 403 Forbidden |
| Provider tracing ID | X-Provider-Id header | Not an auth check; used only as correlation fallback | No auth grant |
Provider identity resolution order used by service:
azpappidclient_idX-Provider-Idunknown-provider
6. Common Failure Cases and Fixes
401 Unauthorized: Invalid issuer
Symptoms:
- Token rejected although structurally valid.
Likely cause:
issclaim does not match configuredProviderJwt:Authority.
Fix:
- Ensure provider uses the correct tenant/authority.
- Validate token
issclaim and environment configuration. - Re-issue token from correct Entra tenant/application context.
401 Unauthorized: Invalid audience
Symptoms:
- Token acquisition succeeds, API still returns 401.
Likely cause:
- Token
auddoes not matchProviderJwt:Audience.
Fix:
- Verify
scope=<provider-api-app-id-uri>/.defaultis correct. - Inspect emitted
audclaim from issued token. - Align runtime
ProviderJwt:Audiencewith actual emitted audience model for the environment.
403 Forbidden: Missing role
Symptoms:
- Token is accepted at authentication level but request is forbidden.
Likely cause:
- Token does not contain
roles=ProviderApi.Access.
Fix:
- Confirm app role exists on Provider API app registration.
- Confirm provider client app has assigned application permission/app role.
- Re-consent/re-issue token and verify
rolesclaim includesProviderApi.Access.
403 Forbidden: Client ID not allowed
Symptoms:
- Only some provider clients fail with 403 in restricted environments.
Likely cause:
ProviderJwt:AllowedClientIdsis enabled and token client identity claim is not in allow-list.
Fix:
- Extract client identity from token (
azpthenappidthenclient_id). - Add correct client ID to
ProviderJwt:AllowedClientIds. - Re-test after configuration rollout.
7. Security and Operations Best Practices
- Use one client app registration per provider for isolation.
- Prefer certificate credentials over long-lived client secrets.
- Apply least privilege: only assign
ProviderApi.Accesswhere required. - Treat
X-Provider-Idas non-security metadata only. - Implement credential rotation policy (owner, cadence, expiry alerts).
- Maintain auditable mapping of provider to client ID and environment access.
- Use per-provider revocation by disabling/removing role assignment or credential.
- Validate authorization continuously in non-production smoke tests before production rollout.
Environment Template
Use this block for each environment hand-off:
Environment: <ENV_NAME>
API base URL: <API_BASE_URL>
Authority: <AUTHORITY>
Token endpoint: <TOKEN_ENDPOINT>
Token scope: <PROVIDER_API_APP_ID_URI>/.default
Expected audience (aud): <API_AUDIENCE>
Required role: ProviderApi.Access
Allowed client ID enforcement: <true|false>
References
documentation/v2/api/11_PROVIDER_API_AUTHORIZATION_PROCESS_PROMPT.mddocumentation/v2/api/09_PROVIDER_AUTH_ONBOARDING_GUIDE.mddocumentation/v2/api/16_PROVIDER_API_NOVA_DATA_MAPPING.mdopenapi/provider-controls.openapi.yamlservices/data-service/Program.csservices/data-service/Controllers/ProviderControlsController.cs