Umfangreiche Erweiterung der Skill-Bibliothek: Neue Skills für Humanisierung (Englisch/PT-BR), Design-Validierung, AI-SEO und Coolify-Deployment inkl. Regelwerke, Presets, Pattern-Referenzen, Testfälle und Automatisierungsskripte. Zusätzliche Skills für Revenue-Centric Design, Pier Cloud, OKF, Lebenslauf- und LinkedIn-Optimierung sowie zahlreiche Referenzdateien, Checklisten und YAML/JSON/Markdown-Templates. Einführung einer vollständigen OpenWiki-Dokumentation mit Architektur-, Domain- und Workflow-Beschreibungen, zentralem Index und automatisierten Updates. Modularer Aufbau, restriktive Lizenzen und umfassende Qualitäts- und Evaluationsmechanismen für alle neuen Inhalte.
17 KiB
Server-Side Implementation Guide
Detailed guidance for implementing auth.md protocol endpoints on the backend (v2, June 2026). Covers discovery, registration, claim ceremony, token exchange, revocation, security, rate limiting, and audit events.
Table of Contents
- Minimum Implementation
- Discovery Documents
- POST /agent/identity — Registration Handler
- ID-JAG Verification
- Claim Ceremony
- POST /oauth2/token — Token Endpoint
- Revocation
- User Matching and JIT Provisioning
- Rate Limiting
- Security
- Audit Events
- Deploy Checklist
Minimum Implementation
- Publish
/.well-known/oauth-protected-resourcewithresource_nameandresource_logo_uri - Publish
/.well-known/oauth-authorization-serverwithissuer,token_endpoint,revocation_endpoint,grant_types_supported, andagent_authblock - Return
WWW-Authenticate: Bearer resource_metadata="..."on 401 responses - Host
POST /agent/identitythat dispatches on thetypefield - For identity_assertion: maintain a trust list and verify ID-JAG signatures via JWKS, validate
auth_time - For service_auth: return
claimblock withuser_code+verification_uri - For anonymous: issue
identity_assertionimmediately with pre-claim scopes - Host
POST /agent/identity/claimfor deferred claim initiation - Implement
POST /oauth2/tokenhandling both grant types (jwt-bearer exchange + claim polling) - Implement
POST /oauth2/revokefor credential-layer revocation (RFC 7009) - Accept SETs at
events_endpointfor registration-layer revocation (RFC 8935) - Record audit events for every state change
Discovery Documents
Serving the PRM
GET /.well-known/oauth-protected-resource
→ 200 OK
→ Content-Type: application/json
Must include resource_name and resource_logo_uri — agents surface these to the user for consent before asserting identity.
Cache aggressively: Cache-Control: public, max-age=3600.
Serving the AS Metadata
GET /.well-known/oauth-authorization-server
→ 200 OK
→ Content-Type: application/json
Must include standard OAuth fields (issuer, token_endpoint, revocation_endpoint, grant_types_supported) plus the agent_auth block with identity_endpoint, claim_endpoint, events_endpoint.
WWW-Authenticate Header
On every 401 response from the API:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.service.com/.well-known/oauth-protected-resource"
POST /agent/identity — Registration Handler
All registration requests share the same endpoint and dispatch on the type field:
POST /agent/identity
Content-Type: application/json
Dispatch
type |
Flow | Returns |
|---|---|---|
identity_assertion |
ID-JAG verified | identity_assertion (service-signed JWT) |
service_auth |
Email hint + browser ceremony | claim block (ceremony materials) |
anonymous |
No identity | identity_assertion + claim_token for deferred claim |
Handler: identity_assertion + id-jag
- Decode the ID-JAG header to obtain
kidandalg - Look up the issuer (
iss) in the trust list. Reject if unknown →issuer_not_enabled - Fetch JWKS from the provider (see ID-JAG Verification section for caching)
- Verify signature →
invalid_requestif fails - Validate claims:
audmatches theresourcefrom PRM →invalid_requestexpis in the future →invalid_requestiatnot unreasonably in the future (~1-2 min skew)jtinot seen recently →invalid_request(replay)auth_timepresent and withinidJagMaxAuthAgeSeconds→login_required(401) if too old- At least
email_verifiedorphone_number_verifiedistrue→invalid_request
- Match or provision the user (see User Matching)
- Check delegation: if
(iss, sub)is known OR JIT-provisioned without collision → success - If email/phone collision with existing account but no delegation on file →
interaction_required(401) withclaimblock - On success: sign an
identity_assertionJWT and return it
Handler: service_auth
- Validate
login_hint(email format) - Create a registration row with type
service_auth - Generate
claim_token(returned to agent once),user_code(6-digit),claim_attempt_token(embedded in verification_uri) - Store SHA-256 hashes of
claim_token - Build
verification_uripointing to the service's login page withreturn_toparameter - Return the registration response with
claimblock containinguser_code,verification_uri,expires_in,interval
Handler: anonymous
- Apply rate limits
- Create a registration row
- Sign an
identity_assertionJWT with pre-claim scopes - Generate
claim_tokenfor deferred claim. Store only SHA-256 hash. - Return
identity_assertion+claim_token+ pre/post claim scopes
ID-JAG Verification
Trust List
Maintain a registry of providers. Minimum entry: issuer URL. Richer entries can pin JWKS URI, CIMD URL, or attestation policy.
JWKS Fetching
- Fetch
{iss}/.well-known/jwks.jsonon first use - Cache per
Cache-Control, with floor 10 min, ceiling 24h - On
kidmiss, refetch once before rejecting
CIMD Resolution
If client_id is a URL:
- Fetch as OAuth Client ID Metadata Document
- Verify
jwks_urimatches the one used to verify signature
auth_time Validation
auth_timeis required in ID-JAGs- Compare
now() - auth_timeagainstidJagMaxAuthAgeSeconds(service-configured, e.g., 3600) - If too old: return
login_required(401) withmax_agein the response - Agent must re-authenticate user at provider and mint fresh ID-JAG
Replay Protection
- Cache
jtivalues with TTL of at leastexp - iat+ clock skew (typically 6 min) - Reject on collision with
invalid_request
Claim Ceremony
The v2 claim ceremony is browser-based, borrowing from RFC 8628 device authorization.
POST /agent/identity/claim (anonymous deferred claim)
- Hash
claim_token, look up registration - Reject if not found →
invalid_claim_token, already claimed →claimed_or_in_flight, expired →claim_expired - Generate
user_code(6-digit),claim_attempt_token - Build
verification_uriwith embeddedclaim_attempt_token - Return
claim_attemptblock withuser_code,verification_uri,expires_in,interval
Service-Hosted Claim Page
When the user opens verification_uri:
- Redirect to login if not authenticated
- After login, show claim page displaying:
- The user's identity ("You're signed in as jane@example.com")
- The requesting agent/provider info (from PRM
resource_name) - Input field for the 6-digit code
- On correct code submission: mark claim as complete, associate registration with user
- Incorrect code: show error, allow retry (up to limit)
Claim Completion Flow
When the user submits the correct user_code on the claim page:
- Mark the claim as complete in the database
- For anonymous: sign a new
identity_assertion(v2) carrying user claims, superseding the pre-claim one - For service_auth: sign the first
identity_assertionfor this registration - The next poll at
/oauth2/tokenwith the claim grant returns the access_token + identity_assertion
POST /oauth2/token — Token Endpoint
Handles two grant types at the same endpoint:
Grant: urn:ietf:params:oauth:grant-type:jwt-bearer (Token Exchange)
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion=<identity_assertion>
&resource=<resource_url> (optional but recommended)
Processing:
- Verify the
identity_assertionJWT signature (service's own key) - Validate
expnot passed →invalid_grantif expired - Check assertion not revoked →
invalid_grantif revoked - Mint a fresh
access_tokenscoped to the assertion's scopes - Return standard OAuth token response
Grant: urn:workos:agent-auth:grant-type:claim (Claim Polling)
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:workos:agent-auth:grant-type:claim
&claim_token=<claim_token>
Processing:
- Hash
claim_token, look up registration - If claim not yet completed → return
authorization_pending - If user_code window expired → return
expired_token - If polling too fast → return
slow_down - If claim completed → mint access_token + return along with new
identity_assertion
Why a profile-specific grant URN? So this doesn't collide with services that also implement standard RFC 8628 device authorization at the same token endpoint.
Error Responses
| Error | HTTP | Condition |
|---|---|---|
invalid_grant |
400 | Assertion expired, revoked, or invalid |
invalid_client |
401 | client_id not recognized |
unsupported_grant_type |
400 | Not one of the two supported grants |
authorization_pending |
400 | Claim polling — user hasn't finished |
expired_token |
400 | user_code window closed |
slow_down |
400 | Polling too fast — add ≥5s |
Revocation
Credential Layer — POST /oauth2/revoke (RFC 7009)
Agent-callable. Kills one access_token.
POST /oauth2/revoke
Content-Type: application/x-www-form-urlencoded
token=<access_token>&token_type_hint=access_token
- Return 200 on success (idempotent)
- The underlying
identity_assertionremains valid — agent can re-exchange for a new access_token
Registration Layer — Events Endpoint (RFC 8935)
Provider-driven. Receives Security Event Tokens.
POST /agent/event/notify
Content-Type: application/secevent+jwt
<SET JWT>
Processing:
- Verify SET signature against issuer's JWKS
- Enforce
jtiuniqueness - Match
eventskey to supported schemas - For
https://schemas.workos.com/events/agent/auth/identity/assertion/revoked:- Invalidate all identity_assertions for
(iss, sub, aud) - Invalidate all derived access_tokens
- Invalidate all identity_assertions for
- Return 200 on success, 400 on verification failure
Bulk Revocation
Provide an operator-facing mechanism to revoke all outstanding identity_assertions and access_tokens for a tenant in one shot — for incident response.
User Matching and JIT Provisioning
Resolution order:
- Delegation record match — if
(iss, sub)has a delegation on file, route to that user. Strongest identifier. - Verified email match — if a user exists with same verified email:
- If delegation exists for
(iss, sub)→ direct match (already covered above) - If NO delegation for
(iss, sub)→interaction_required(401). User must confirm linking.
- If delegation exists for
- Verified phone match — same pattern.
- No match → JIT — create a new user per provisioning policy, or refuse.
Reject ID-JAGs with neither verified email nor verified phone.
Rate Limiting
Two Tiers
| Tier | Checked | Default Anonymous | Default identity_assertion |
|---|---|---|---|
| Per-IP | First | 5/hour | 60/hour |
| Per-tenant | Second | 100/hour | 1000/hour |
Implementation
- Sliding-window counter with shared store
- Fail open on store errors
- Return 429 with
Retry-Afterheader - Also rate-limit
/oauth2/tokenpolling (respectintervalfrom claim block, reject withslow_down)
Security
Token Hashing
| Token | Storage | Plaintext leaves server |
|---|---|---|
claim_token |
SHA-256 hash | Once, in the registration response |
user_code |
Stored for comparison | Displayed on claim page when user submits |
identity_assertion |
Full JWT stored (or just signature hash for lookup) | In registration response |
claim_token
- Returned exactly once to the agent in the registration response
- Agent holds in memory for ceremony duration — must not persist past Step 4
- High-entropy: prefix
clm_+ 25+ chars base62
auth_time Enforcement
- Service configures
idJagMaxAuthAgeSeconds(default: 3600) - Reject ID-JAGs where
now() - auth_time > idJagMaxAuthAgeSeconds - Return
login_required(401) withmax_agefield
Consent UX
- Surface
resource_nameandresource_logo_urifrom PRM to user before identity assertion - The claim page should display who is requesting access (provider name from CIMD or ID-JAG metadata)
user_code Security
- 6-digit numeric code
- 10-minute TTL (configurable via
expires_in) - Tight retry limits (3-5 attempts) on the claim page
- Code is tied to the
claim_attempt_tokenembedded inverification_uri
Replay Protection
jticache mandatory for ID-JAGsjtiuniqueness enforced for SETs at events_endpoint- Shared store required for multi-replica deployments
Trust List Discipline
Treat the trusted-providers list as security-critical configuration. Changes should be audited.
Audit Events
Recommended Events
| Event | When | Minimum Data |
|---|---|---|
registration.created |
Successful POST /agent/identity | registration_id, registration_type, iss, sub (if ID-JAG) |
registration.interaction_required |
401 interaction_required returned | registration_id, iss, sub, matched_user_id |
registration.login_required |
401 login_required returned | iss, sub, auth_time, max_age |
claim.initiated |
/agent/identity/claim called | registration_id, email |
claim.completed |
User submitted correct user_code | registration_id, claimed_by_user_id |
claim.expired |
user_code window or registration expired | registration_id |
token.exchanged |
/oauth2/token jwt-bearer success | registration_id, access_token_id |
token.revoked |
/oauth2/revoke called | access_token_id |
registration.revoked |
SET processed at events_endpoint | registration_id, iss, sub |
registration.expired |
Unclaimed registration past TTL | registration_id |
Deploy Checklist
Before publishing
- PRM served at
/.well-known/oauth-protected-resourcewithresource_nameandresource_logo_uri - AS metadata served at
/.well-known/oauth-authorization-serverwithissuer,token_endpoint,revocation_endpoint,grant_types_supported, andagent_authblock agent_authcontainsidentity_endpoint,claim_endpoint,events_endpointauth.mdserved at the domain root- API returns
WWW-Authenticateheader on 401s POST /agent/identitydispatches correctly bytype- Trust list configured (if identity_assertion)
- JWKS fetching with cache (if identity_assertion)
auth_timevalidation againstidJagMaxAuthAgeSeconds(if identity_assertion)POST /agent/identity/claimgenerates user_code + verification_uri (if service_auth/anonymous)- Claim page served at verification_uri (login → code input → confirm)
POST /oauth2/tokenhandles jwt-bearer grant (assertion → access_token)POST /oauth2/tokenhandles claim grant (polling → access_token + identity_assertion)POST /oauth2/revokekills access_tokens (RFC 7009)- Events endpoint accepts SETs for registration revocation
- Rate limiting active on
/agent/identityand/oauth2/token - claim_token stored as SHA-256 hash
- Replay protection for
jtiimplemented - Audit events being recorded
Recommended tests
- identity_assertion: valid ID-JAG with fresh auth_time → identity_assertion returned
- identity_assertion: expired ID-JAG →
invalid_request - identity_assertion: auth_time too old →
login_required(401) - identity_assertion: email collision without delegation →
interaction_required(401) with claim block - identity_assertion: repeated
jti→invalid_request(replay) - identity_assertion: unknown issuer →
issuer_not_enabled - service_auth: valid email → registration with claim block (user_code + verification_uri)
- anonymous: registration → identity_assertion with pre_claim_scopes
- anonymous: claim initiation → claim_attempt with user_code
- /oauth2/token jwt-bearer: valid assertion → access_token
- /oauth2/token jwt-bearer: expired assertion →
invalid_grant - /oauth2/token claim: before completion →
authorization_pending - /oauth2/token claim: after completion → access_token + identity_assertion
- /oauth2/token claim: after window expires →
expired_token - /oauth2/token claim: too-fast polling →
slow_down - /oauth2/revoke: valid access_token → 200
- events_endpoint: valid SET → registrations revoked
- Rate limit exceeded → 429
- 401 on API → WWW-Authenticate header present