Skip to main content
Version: v0.25.0 (Latest)

Hosted sign-in and external federation

An authorization server is a tenant-owned resource with a stable UUID. A resource is either hosted by VDX or operated externally. The slug remains useful for routes and display, but it is not an API identity.

This guide supersedes earlier tenant identity-provider registry procedures. There is no separate identity-provider lifecycle API. OpenID Federation trust-chain administration and dynamic client registration are also outside this bilateral OpenID Connect brokering model.

A hosted resource can authenticate users locally, through selected upstream OIDC providers, or through both methods. An external resource can authorize OID4VCI flows when its discovery document advertises the required grants. It can authenticate users only when it is a validated OIDC provider and an enabled federation binding connects it to a hosted resource.

External authorization servers and federation bindings do not consume a hosted authorization-server license quota. Creating or reactivating a hosted resource does.

Activate the tenant owner and establish an operator session

Before administering a tenant's authorization servers, activate the tenant owner and establish an authenticated operator session. Resolve the invitation first, then complete it with the owner's new password.

Resolve tenant owner activation

Endpoint: POST /api/account-actions/v1/resolve

Captured response: 200 OK

This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.

Live against connected environment

Connect an environment to rewrite this call to real service bases and run it.

Complete tenant owner activation

Endpoint: POST /api/account-actions/v1/complete

Captured response: 200 OK

This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.

Live against connected environment

Connect an environment to rewrite this call to real service bases and run it.

Start the authorization-code flow with PKCE, follow the returned login route, submit the owner's credentials, resume the authorization request, and exchange the response-derived code at the token endpoint.

Start tenant owner authorization

Endpoint: GET /as/acme/authorize

Captured response: 302 Found

This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.

Live against connected environment

Connect an environment to rewrite this call to real service bases and run it.

Open tenant owner login page

Endpoint: GET /as/acme/login

Captured response: 200 OK

This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.

Live against connected environment

Connect an environment to rewrite this call to real service bases and run it.

Submit tenant owner credentials

Endpoint: POST /as/acme/login

Captured response: 302 Found

This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.

Live against connected environment

Connect an environment to rewrite this call to real service bases and run it.

Resume tenant owner authorization

Endpoint: GET /as/acme/authorize/callback

Captured response: 302 Found

This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.

Live against connected environment

Connect an environment to rewrite this call to real service bases and run it.

Exchange tenant owner authorization code

Endpoint: POST /as/acme/token

Captured response: 200 OK

This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.

Live against connected environment

Connect an environment to rewrite this call to real service bases and run it.

The examples are sanitized captures. Follow each redirect and use the session identifier, authorization code, and other values returned by your own deployment. Passwords and protocol secrets are redacted. The displayed access and ID tokens have redacted signatures and demonstrate response shape only; they are not verifiable signed artifacts.

Resource and binding model

The model has two explicit layers.

  1. Create an external authorization-server resource. Validation fetches its OAuth authorization-server metadata and OpenID Provider metadata, verifies issuer consistency, records the supported capabilities, and assigns a freshness period.
  2. Create a federation binding below a hosted authorization server. The binding selects the external resource, requested scopes, client authentication method, claim mapping, order, and enabled state.

This separation allows one external provider to protect OID4VCI without becoming a sign-in provider. It also allows the same validated provider to be attached to more than one hosted authorization server without copying discovery state or credentials.

Authorization-server REST surface

All management operations are tenant scoped below the platform-config API:

GET    /api/platform/config/v1/tenants/{tenantId}/authorization-servers
POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers
GET /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{authorizationServerId}
PUT /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{authorizationServerId}
DELETE /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{authorizationServerId}

POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{authorizationServerId}/activate
POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{authorizationServerId}/suspend
POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{authorizationServerId}/decommission
POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{authorizationServerId}/validate
POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{authorizationServerId}/discovery/refresh

Create a hosted or external authorization-server resource. The response contains the stable authorizationServerId used by every nested operation.

POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers

The full request and response schema is in the API reference, or open the API reference tab to read it inline.

The platform tenant is not accepted by this tenant resource surface. Its built-in authorization server remains isolated.

Create a hosted authorization server

Create a hosted resource when VDX will own the issuer, protocol endpoints, clients, identities, and signing configuration. The authentication mode determines which routes can satisfy an authorization request.

POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers
Content-Type: application/json

{
"slug": "workforce-broker",
"displayName": "Workforce broker",
"issuer": "https://tenant.example.com/as/workforce-broker",
"deployment": "HOSTED",
"authenticationMode": "HYBRID",
"purposes": ["GENERAL", "CREDENTIAL_ISSUANCE", "WALLET_LOGIN"],
"usages": [],
"allowedGrantTypes": ["authorization_code", "refresh_token"]
}

The response contains the resource UUID and revision. Use both values for subsequent operations. Lifecycle requests carry the observed revision so a stale administrator session cannot overwrite a later change.

POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{authorizationServerId}/activate
Content-Type: application/json

{
"expectedRevision": 3
}

Add and validate an external provider

Create the external resource with its issuer and the capabilities you expect discovery to prove. The issuer is the only discovery input. Creation discovers and reconciles the issuer before it commits the resource, so a discovery failure leaves no partial resource. The committed resource still requires an explicit activation before it can be selected. Do not attach it to hosted sign-in until you have inspected the discovery result.

POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers
Content-Type: application/json

{
"slug": "workforce",
"displayName": "Workforce identity provider",
"issuer": "https://login.example.com/tenant/v2.0",
"deployment": "EXTERNAL",
"expectedCapabilities": ["OAUTH2", "OIDC"],
"purposes": ["GENERAL"],
"usages": ["HOSTED_LOGIN_UPSTREAM"],
"allowedGrantTypes": ["authorization_code"]
}

Call validateExternalAuthorizationServer to validate the configured issuer without changing the resource, or refreshExternalAuthorizationServerDiscovery to replace a previous discovery snapshot. Discovery rejects non-HTTPS sources, redirects, private or local addresses, DNS rebinding, oversized responses, issuer mismatches, and inconsistent OAuth and OpenID metadata. A successful response records the digest, source URLs, validation time, expiry time, capabilities, grants, scopes, endpoints, and supported client-authentication methods. For an OIDC provider, the reconciled UserInfo endpoint is retained so the callback can require its sub to match the validated ID Token subject. When the provider publishes an RFC 7591 endpoint, the snapshot retains it as discovery evidence only. Dynamic client registration is outside this release.

Activation does not override discovery freshness. An external resource with stale or invalid discovery cannot be used by an enabled federation binding.

The external resource response contains its reconciled metadata snapshot and digest. It does not contain an upstream client secret. Upstream client credentials belong to a federation binding and are represented on reads only by their approved typed reference.

Attach an upstream provider to hosted sign-in

Federation bindings are nested below the hosted authorization server that will broker sign-in:

GET    /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{hostedId}/federation-bindings
POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{hostedId}/federation-bindings
PUT /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{hostedId}/federation-bindings/order
GET /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{hostedId}/federation-bindings/{bindingId}
PATCH /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{hostedId}/federation-bindings/{bindingId}
DELETE /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{hostedId}/federation-bindings/{bindingId}
POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{hostedId}/federation-bindings/{bindingId}/validate
POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{hostedId}/federation-bindings/{bindingId}/enable
POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{hostedId}/federation-bindings/{bindingId}/disable

A binding can be enabled only when both resources belong to the same tenant, the source is hosted and active, the target is external and active, fresh OIDC discovery is present, every requested scope is supported, and the selected client authentication method is advertised.

Create the binding disabled. The operator-provisioned client identifier and write-once credential belong to the binding, not to the external resource:

POST /api/platform/config/v1/tenants/{tenantId}/authorization-servers/{hostedId}/federation-bindings
Content-Type: application/json

{
"externalAuthorizationServerId": "11111111-1111-4111-8111-111111111111",
"order": 0,
"enabled": false,
"scopes": ["openid", "profile", "email"],
"claimsMapping": {
"sub": "subject",
"email": "email",
"name": "displayName"
},
"clientAuthentication": {
"method": "client_secret_basic",
"clientId": "tenant-hosted-as",
"secretValue": "<write-once-secret>"
}
}

The binding order controls the provider chooser. A hosted resource in hybrid mode can offer local sign-in alongside its enabled upstream bindings. A hosted resource in federated-only mode requires at least one valid enabled binding.

Authorization route behavior

LOCAL_ONLY presents local authentication. FEDERATED_ONLY redirects directly when exactly one eligible binding exists and presents the ordered upstream chooser when several exist. HYBRID presents local authentication and the ordered upstream choices together.

An existing session is evaluated against the request's prompt, max_age, client, issuer, and account state. prompt=none never renders a local form or provider chooser. The server continues an eligible session or returns the protocol error to the registered client redirect URI.

The downstream authorization transaction remains authoritative while the upstream round trip uses independent state, nonce, and PKCE. The callback validates the exact issuer, ID Token signature, audience, authorized party, nonce, timestamps, endpoint origin, selected binding, and transaction before resuming the downstream request. Provider, tenant, binding, redirect, and issuer substitutions fail closed.

Successful local or federated authentication produces normalized evidence. The evidence records the hosted authorization-server UUID, optional federation-binding UUID, upstream issuer and subject, assurance values, authentication time, governed claim provenance, and transaction references. It does not store raw upstream tokens, authorization codes, client secrets, or unrelated claims. Wallet identity reconciliation is not part of this release.

Client authentication and secrets

An upstream client can use none, client_secret_basic, client_secret_post, or private_key_jwt when discovery advertises that method.

Raw secrets are accepted only on a write operation that immediately stores them through secret management. Read responses never contain the raw value. An administrator can instead select an existing typed secret reference. Private-key JWT uses a typed KMS resource and key alias reference. A credential or key reference must belong to the same tenant as the binding.

Provision the upstream client with the provider before creating the binding. Enter the resulting client identifier and either a write-once secret value, an existing typed secret reference, or a typed KMS reference. The API never registers a client dynamically, derives a registration endpoint from the issuer, or returns a raw secret.

Claims mapping

The binding claim map translates upstream source claims into governed hosted-authentication targets. Its direction is source claim → governed target: sub identifies the stable upstream principal, email provides the email target, and name provides the displayName target. Cached upstream UserInfo retains the source claims; this binding map is for authentication evidence and is not a credential-design attribute map.

For authorization-code issuance, the hosted authorization server's UserInfo endpoint remains available according to its configuration. The tenant opts in with oid4vci.issuer.surface-local-userinfo-to-issuance=true when the issuer should forward UserInfo values, including cached federated claims, into issuance. The default profile selection includes given_name, family_name, and job_title; email requires the separate email scope. employee_id is not implied: allow it explicitly with claims.userinfo.employee_id or an explicitly configured scope mapping. The access token may intentionally be identity-free.

UserInfo values then enter the separate AuthSessionClaimSource issuance-pipeline step. claimsMapping alone does not write credential claims; credential designs and the governed pipeline claim selection decide which authenticated values become credential attributes.

The hosted authorization transaction records which binding authenticated the user and keeps normalized authentication evidence. Credential issuance can consume that evidence through its governed attribute pipeline, but the federation binding does not write arbitrary credential claims.

Safe operating sequence

  1. Create the external resource and inspect the discovery result returned by creation.
  2. Validate discovery and inspect the recorded issuer, endpoints, grants, scopes, and authentication methods.
  3. Activate the external resource.
  4. Create a disabled binding below the intended hosted authorization server.
  5. Write or select the client credential reference and configure claims.
  6. Validate the binding.
  7. Enable it and verify the provider chooser with a real authorization request.
  8. Keep local sign-in available until the upstream flow has been verified for the tenant.

Disabling a binding stops new upstream selections without deleting discovery or client configuration. Suspending the external resource disables its use across all bindings. Decommissioning is terminal and should be used only after dependent bindings and issuer selections have been removed.

Migration from the retired IdP registry

During database upgrade, each retired tenant_idp row becomes a deterministic external OIDC resource. The migrated resource is suspended, disabled, and unbound even when the old row was enabled. Its non-secret client ID, requested scopes, claims mapping, and previous enabled state are retained in the migration ledger for remediation. Raw credentials and secret locators are never exposed through that ledger.

An administrator must validate discovery, write or select a supported client credential reference, create the intended hosted binding, validate it, and enable it. The retired tenant_idp authority is removed only after the complete tenant conversion succeeds. A failed conversion leaves the old rows intact and records a sanitized failure in the durable migration ledger.

The hosted oauth2.servers.<slug>.* values remain as generated runtime configuration. They are no longer the lifecycle authority. The stable authorization-server resource and its configuration binding are authoritative.

Troubleshooting

SymptomWhat to inspectCorrective action
External creation or refresh rejects the issuerHTTPS issuer, DNS answers, redirect behavior, certificate trust, metadata size, and the issuer values in both metadata documentsCorrect the upstream issuer or transport. Do not add a manual endpoint override or weaken discovery checks.
Activation reports stale discoverySnapshot freshness, validation time, digest, and the current upstream metadataRefresh discovery, inspect the new snapshot, and retry with the new resource revision.
A binding cannot be enabledResource lifecycles, OIDC capability, snapshot freshness, scopes, authentication method, credential reference, and binding validation statusRepair the stated dependency, validate the binding, then use the dedicated enable operation.
Federated-only sign-in has no usable routeEnabled binding count, binding order, external resource state, and discovery freshnessRestore and validate at least one binding. Do not enable a fallback provider URL outside the resource model.
prompt=none returns an errorExisting session age and validity, requested client, hosted issuer, and account stateEstablish an eligible session interactively. A chooser must not be rendered for this request.
The upstream callback is rejectedExact issuer, audience, authorized party, nonce, timestamps, state, PKCE verifier, and selected binding revisionRestart the authorization request after correcting the upstream client or metadata. Do not replay or edit callback parameters.
A confidential credential is not visible after saveThe credential method and typed secret or KMS reference shown on the bindingThis is expected. Rotate through a write operation if the secret is unknown; raw credential readback is prohibited.

Use Authorization-server migration remediation when an upgrade ledger entry is failed. Use Authorization-server selection and protocol profiles when an issuer binding, offer, or profile upgrade is blocked.