Neue Skills, Referenzen & OpenWiki-Doku integriert

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.
This commit is contained in:
Tim Krampitz
2026-07-26 14:00:58 +02:00
parent 070727d5cd
commit 01046b01e4
202 changed files with 31290 additions and 0 deletions

View File

@@ -0,0 +1,433 @@
# 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
1. [Minimum Implementation](#minimum-implementation)
2. [Discovery Documents](#discovery-documents)
3. [POST /agent/identity — Registration Handler](#post-agentidentity--registration-handler)
4. [ID-JAG Verification](#id-jag-verification)
5. [Claim Ceremony](#claim-ceremony)
6. [POST /oauth2/token — Token Endpoint](#post-oauth2token--token-endpoint)
7. [Revocation](#revocation)
8. [User Matching and JIT Provisioning](#user-matching-and-jit-provisioning)
9. [Rate Limiting](#rate-limiting)
10. [Security](#security)
11. [Audit Events](#audit-events)
12. [Deploy Checklist](#deploy-checklist)
---
## Minimum Implementation
1. Publish `/.well-known/oauth-protected-resource` with `resource_name` and `resource_logo_uri`
2. Publish `/.well-known/oauth-authorization-server` with `issuer`, `token_endpoint`, `revocation_endpoint`, `grant_types_supported`, and `agent_auth` block
3. Return `WWW-Authenticate: Bearer resource_metadata="..."` on 401 responses
4. Host `POST /agent/identity` that dispatches on the `type` field
5. For identity_assertion: maintain a trust list and verify ID-JAG signatures via JWKS, validate `auth_time`
6. For service_auth: return `claim` block with `user_code` + `verification_uri`
7. For anonymous: issue `identity_assertion` immediately with pre-claim scopes
8. Host `POST /agent/identity/claim` for deferred claim initiation
9. Implement `POST /oauth2/token` handling both grant types (jwt-bearer exchange + claim polling)
10. Implement `POST /oauth2/revoke` for credential-layer revocation (RFC 7009)
11. Accept SETs at `events_endpoint` for registration-layer revocation (RFC 8935)
12. 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
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
1. Decode the ID-JAG header to obtain `kid` and `alg`
2. Look up the issuer (`iss`) in the trust list. Reject if unknown → `issuer_not_enabled`
3. Fetch JWKS from the provider (see ID-JAG Verification section for caching)
4. Verify signature → `invalid_request` if fails
5. Validate claims:
- `aud` matches the `resource` from PRM → `invalid_request`
- `exp` is in the future → `invalid_request`
- `iat` not unreasonably in the future (~1-2 min skew)
- `jti` not seen recently → `invalid_request` (replay)
- `auth_time` present and within `idJagMaxAuthAgeSeconds``login_required` (401) if too old
- At least `email_verified` or `phone_number_verified` is `true``invalid_request`
6. Match or provision the user (see User Matching)
7. Check delegation: if `(iss, sub)` is known OR JIT-provisioned without collision → success
8. If email/phone collision with existing account but no delegation on file → `interaction_required` (401) with `claim` block
9. On success: sign an `identity_assertion` JWT and return it
### Handler: service_auth
1. Validate `login_hint` (email format)
2. Create a registration row with type `service_auth`
3. Generate `claim_token` (returned to agent once), `user_code` (6-digit), `claim_attempt_token` (embedded in verification_uri)
4. Store SHA-256 hashes of `claim_token`
5. Build `verification_uri` pointing to the service's login page with `return_to` parameter
6. Return the registration response with `claim` block containing `user_code`, `verification_uri`, `expires_in`, `interval`
### Handler: anonymous
1. Apply rate limits
2. Create a registration row
3. Sign an `identity_assertion` JWT with pre-claim scopes
4. Generate `claim_token` for deferred claim. Store only SHA-256 hash.
5. 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.json` on first use
- Cache per `Cache-Control`, with floor 10 min, ceiling 24h
- On `kid` miss, refetch once before rejecting
### CIMD Resolution
If `client_id` is a URL:
1. Fetch as OAuth Client ID Metadata Document
2. Verify `jwks_uri` matches the one used to verify signature
### auth_time Validation
- `auth_time` is **required** in ID-JAGs
- Compare `now() - auth_time` against `idJagMaxAuthAgeSeconds` (service-configured, e.g., 3600)
- If too old: return `login_required` (401) with `max_age` in the response
- Agent must re-authenticate user at provider and mint fresh ID-JAG
### Replay Protection
- Cache `jti` values with TTL of at least `exp - 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)
1. Hash `claim_token`, look up registration
2. Reject if not found → `invalid_claim_token`, already claimed → `claimed_or_in_flight`, expired → `claim_expired`
3. Generate `user_code` (6-digit), `claim_attempt_token`
4. Build `verification_uri` with embedded `claim_attempt_token`
5. Return `claim_attempt` block with `user_code`, `verification_uri`, `expires_in`, `interval`
### Service-Hosted Claim Page
When the user opens `verification_uri`:
1. Redirect to login if not authenticated
2. 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
3. On correct code submission: mark claim as complete, associate registration with user
4. Incorrect code: show error, allow retry (up to limit)
### Claim Completion Flow
When the user submits the correct `user_code` on the claim page:
1. Mark the claim as complete in the database
2. For anonymous: sign a new `identity_assertion` (v2) carrying user claims, superseding the pre-claim one
3. For service_auth: sign the first `identity_assertion` for this registration
4. The next poll at `/oauth2/token` with 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:
1. Verify the `identity_assertion` JWT signature (service's own key)
2. Validate `exp` not passed → `invalid_grant` if expired
3. Check assertion not revoked → `invalid_grant` if revoked
4. Mint a fresh `access_token` scoped to the assertion's scopes
5. 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:
1. Hash `claim_token`, look up registration
2. If claim not yet completed → return `authorization_pending`
3. If user_code window expired → return `expired_token`
4. If polling too fast → return `slow_down`
5. 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_assertion` remains 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:
1. Verify SET signature against issuer's JWKS
2. Enforce `jti` uniqueness
3. Match `events` key to supported schemas
4. For `https://schemas.workos.com/events/agent/auth/identity/assertion/revoked`:
- Invalidate all identity_assertions for `(iss, sub, aud)`
- Invalidate all derived access_tokens
5. 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:
1. **Delegation record match** — if `(iss, sub)` has a delegation on file, route to that user. Strongest identifier.
2. **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.
3. **Verified phone match** — same pattern.
4. **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-After` header
- Also rate-limit `/oauth2/token` polling (respect `interval` from claim block, reject with `slow_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) with `max_age` field
### Consent UX
- Surface `resource_name` and `resource_logo_uri` from 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_token` embedded in `verification_uri`
### Replay Protection
- `jti` cache mandatory for ID-JAGs
- `jti` uniqueness 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-resource` with `resource_name` and `resource_logo_uri`
- [ ] AS metadata served at `/.well-known/oauth-authorization-server` with `issuer`, `token_endpoint`, `revocation_endpoint`, `grant_types_supported`, and `agent_auth` block
- [ ] `agent_auth` contains `identity_endpoint`, `claim_endpoint`, `events_endpoint`
- [ ] `auth.md` served at the domain root
- [ ] API returns `WWW-Authenticate` header on 401s
- [ ] `POST /agent/identity` dispatches correctly by `type`
- [ ] Trust list configured (if identity_assertion)
- [ ] JWKS fetching with cache (if identity_assertion)
- [ ] `auth_time` validation against `idJagMaxAuthAgeSeconds` (if identity_assertion)
- [ ] `POST /agent/identity/claim` generates user_code + verification_uri (if service_auth/anonymous)
- [ ] Claim page served at verification_uri (login → code input → confirm)
- [ ] `POST /oauth2/token` handles jwt-bearer grant (assertion → access_token)
- [ ] `POST /oauth2/token` handles claim grant (polling → access_token + identity_assertion)
- [ ] `POST /oauth2/revoke` kills access_tokens (RFC 7009)
- [ ] Events endpoint accepts SETs for registration revocation
- [ ] Rate limiting active on `/agent/identity` and `/oauth2/token`
- [ ] claim_token stored as SHA-256 hash
- [ ] Replay protection for `jti` implemented
- [ ] 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