OAuthServer configuration
The OAuthServer section of Managed Configuration controls your trust domain's OAuth server: the outside systems it trusts, and the token policy that decides every request. Each trust domain has at most one OAuthServer section.
This page shows the structure of the section, a complete example, and the conventions that apply to every field. Each block has its own page.
Before you start
- Minimum version:
defakto-server0.43.0. The OAuth server is part of the Trust Domain Server, and is available on every self-hosted trust domain running 0.43.0 or later. - If you upgraded an existing trust domain to 0.43.0 or later: Confirm that the OAuth server has signing keys. In the Defakto console, open your trust domain's Trust Bundle view, and look for an OAuth entry under Active Signing Keys. If there isn't one, rotate the trust domain's signing keys to activate the OAuth server. The OAuth server signs tokens with its own signing keys, separate from your SVID signing keys, and an upgraded Trust Domain Server doesn't generate them until its next signing key rotation. A trust domain first deployed with 0.43.0 or later has them from the start.
- Check the network requirements when the OAuth server works with systems outside the network where your Trust Domain Servers run. For example:
- To sign people in, or to validate ID tokens or access tokens from your identity provider, the Trust Domain Server must reach the identity provider to get its signing keys.
- MCP clients that run as cloud services, such as Claude, must reach the OAuth server to get tokens.
- To get tokens for an external service, the Trust Domain Server must reach that service's authorization server.
- If no outside systems are involved: You need only
endpoints.publicBaseURL, so that clients can discover the OAuth server's endpoints. For example, a workload can then exchange its JWT-SVID for an access token to call an MCP server in your trust domain.
Structure
OAuthServer
├── endpoints Where callers and browsers reach the OAuth server
├── identityProviders[] Identity providers that vouch for users
├── oauthClients[] OAuth clients you trust, by their client ID URLs
├── externalResources[] Third-party services that Defakto gets tokens for
├── authorizers[] Your own decision webhook, if you use one
└── policy The token policy: access decisions, scopes, lifetimes, and claims
| Block | Required | Reference |
|---|---|---|
endpoints | When you host your own Trust Domain Servers | Endpoints |
identityProviders | No | Identity providers |
oauthClients | When an identity provider has login | OAuth clients |
externalResources | No | External resources |
authorizers | No | Authorizers |
policy | No | Token policy |
Complete example
section: OAuthServer
schema: v1
spec:
endpoints:
publicBaseURL: https://td-server.defakto.example.com
identityProviders:
- name: corp-idp
issuer: https://idp.example.com
idTokens:
audiences: [web-app]
claimValidationRules:
- claim: email_verified
requiredValue: true
accessTokens:
audiences: [https://api.example.com]
login:
clientID: <client-id-at-your-identity-provider>
clientAuth:
method: private_key_jwt
scopes: [openid, email]
oauthClients:
- clientID: https://claude.ai/oauth/mcp-oauth-client-metadata
externalResources:
- name: vendor-api
resource: https://api.example.net
authorizationServer:
issuer: https://auth.example.net
grant:
clientCredentials:
clientID: <client-id-at-the-service>
clientAuth:
method: client_secret_basic
secretRef:
name: vendor-api
key: clientSecret
scopes: [records.read, records.write]
authorizers:
- name: corp-pdp
webhook:
url: https://pdp.example.com/authorize
auth:
method: bearer
timeout: 2s
policy:
refuseAll: false
authorization:
unmatched: deny
deny:
- name: report-generator-never-acts-for-users
match:
caller:
spiffeID: spiffe://example.com/ns/batch/sa/report-generator
requestedTokenType: urn:defakto:security:token-type:jwt-svid-obo
allow:
- name: agents-call-helpdesk
match:
caller:
spiffeID: spiffe://example.com/ns/agents/**
requestedTokenType: urn:ietf:params:oauth:token-type:access_token
audience: https://helpdesk.example.com/mcp
token:
allowedScopes: [tickets.read, tickets.write]
- name: people-call-helpdesk
match:
caller:
type: oauthClientID
user:
identityProvider: corp-idp
via: login
requestedTokenType: urn:ietf:params:oauth:token-type:access_token
audience: https://helpdesk.example.com/mcp
token:
allowedScopes: [tickets.read]
- name: reporting-reads-vendor-records
match:
caller:
spiffeID: spiffe://example.com/ns/reporting/**
requestedTokenType: urn:ietf:params:oauth:token-type:access_token
audience: https://api.example.net
token:
allowedScopes: [records.read]
externalAuthorize:
- name: pdp-decides-delegations
match:
caller:
spiffeID: spiffe://example.com/ns/agents/**
requestedTokenType: urn:defakto:security:token-type:jwt-svid-obo
authorizer: corp-pdp
token:
maxTTL: 2m
Applying the configuration
OAuthServer is a trust domain section. You can apply Managed Configuration through the Defakto API, Terraform, or the Defakto CLI. See applying configuration. For example, with the CLI:
defakto config set trust-domain --id <trust-domain-id> oauth-server.yaml
Defakto checks the whole section when you apply it, and rejects it with an error that names the problem. Nothing in a rejected configuration takes effect.
After you apply a change, the OAuth server's metadata can take a few minutes, and up to an hour, to show it. For example, a new publicBaseURL or a newly added sign-in appears in the metadata after this delay.
Conventions
These conventions apply to every field in the section:
- Names. Every
nameinidentityProviders,externalResources, andauthorizersis a DNS label, such ascorp-idp, and is unique across all three lists, so a name always refers to one entry. Rule names in the token policy are also DNS labels, unique across all rules. - One value or a list. Where a field takes "one or a list", you can write a single value or a list. A list matches any of its values.
- No empty values. Empty strings, empty lists, empty objects, and
nullare rejected. - No duplicate keys. A key that appears twice at the same level is rejected, rather than one value silently replacing the other.
- URLs. Issuer and endpoint URLs are absolute
httpsURLs, with no user information, query, or fragment.
Next steps
Every configuration starts with endpoints. Then add the blocks your scenarios need:
- Identity providers, for flows that involve people
- OAuth clients, to choose which OAuth clients you trust
- External resources, for tokens for third-party services
- Authorizers, to make decisions with your own webhook
- Token policy, to decide who can get which tokens