Skip to main content

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:

  1. Provider backend (confidential client) requests a token from Microsoft Entra.
  2. Entra issues an access token for the Provider API resource.
  3. Provider calls the Data Service Provider API with Authorization: Bearer <access_token>.
  4. Data Service validates issuer, audience, token lifetime, required role, and optional client allow-list.
  5. 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

  1. Confirm the Provider API app registration and exposed app role ProviderApi.Access.
  2. Confirm runtime auth settings in target environment:
    • ProviderJwt:Authority
    • ProviderJwt:Audience
    • ProviderJwt:AllowedClientIds (if enabled)
  3. Create a dedicated Entra confidential client app registration for the provider.
  4. Assign Provider API application permission/app role ProviderApi.Access to that client app.
  5. Create provider credential:
    • Preferred: certificate
    • Acceptable for lower environments: client secret
  6. 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

  1. Acquire a token with client credentials.
  2. Verify token contains expected aud and role claim.
  3. Verify provider client identity claim (azp/appid/client_id) is allow-listed when enabled.
  4. Execute smoke calls:
    • POST /api/provider/v1/controls returns 201 or 200 duplicate.

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 ItemToken/Header SourceRuntime CheckOutcome if Fails
Issueriss claimMust match ProviderJwt:Authority401 Unauthorized
Audienceaud claimMust match ProviderJwt:Audience401 Unauthorized
Expirationexp claimToken must be valid (not expired / valid lifetime)401 Unauthorized
Required roleroles claimMust include ProviderApi.Access403 Forbidden
Allowed client IDs (optional)azp or appid or client_idMust be in ProviderJwt:AllowedClientIds when configured403 Forbidden
Provider tracing IDX-Provider-Id headerNot an auth check; used only as correlation fallbackNo auth grant

Provider identity resolution order used by service:

  1. azp
  2. appid
  3. client_id
  4. X-Provider-Id
  5. unknown-provider

6. Common Failure Cases and Fixes

401 Unauthorized: Invalid issuer

Symptoms:

  • Token rejected although structurally valid.

Likely cause:

  • iss claim does not match configured ProviderJwt:Authority.

Fix:

  1. Ensure provider uses the correct tenant/authority.
  2. Validate token iss claim and environment configuration.
  3. Re-issue token from correct Entra tenant/application context.

401 Unauthorized: Invalid audience

Symptoms:

  • Token acquisition succeeds, API still returns 401.

Likely cause:

  • Token aud does not match ProviderJwt:Audience.

Fix:

  1. Verify scope=<provider-api-app-id-uri>/.default is correct.
  2. Inspect emitted aud claim from issued token.
  3. Align runtime ProviderJwt:Audience with 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:

  1. Confirm app role exists on Provider API app registration.
  2. Confirm provider client app has assigned application permission/app role.
  3. Re-consent/re-issue token and verify roles claim includes ProviderApi.Access.

403 Forbidden: Client ID not allowed

Symptoms:

  • Only some provider clients fail with 403 in restricted environments.

Likely cause:

  • ProviderJwt:AllowedClientIds is enabled and token client identity claim is not in allow-list.

Fix:

  1. Extract client identity from token (azp then appid then client_id).
  2. Add correct client ID to ProviderJwt:AllowedClientIds.
  3. Re-test after configuration rollout.

7. Security and Operations Best Practices

  1. Use one client app registration per provider for isolation.
  2. Prefer certificate credentials over long-lived client secrets.
  3. Apply least privilege: only assign ProviderApi.Access where required.
  4. Treat X-Provider-Id as non-security metadata only.
  5. Implement credential rotation policy (owner, cadence, expiry alerts).
  6. Maintain auditable mapping of provider to client ID and environment access.
  7. Use per-provider revocation by disabling/removing role assignment or credential.
  8. 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.md
  • documentation/v2/api/09_PROVIDER_AUTH_ONBOARDING_GUIDE.md
  • documentation/v2/api/16_PROVIDER_API_NOVA_DATA_MAPPING.md
  • openapi/provider-controls.openapi.yaml
  • services/data-service/Program.cs
  • services/data-service/Controllers/ProviderControlsController.cs