Skip to content

docs: rewrite developer docs for integrator audience#169

Merged
appleboy merged 1 commit intomainfrom
worktree-docs
Apr 22, 2026
Merged

docs: rewrite developer docs for integrator audience#169
appleboy merged 1 commit intomainfrom
worktree-docs

Conversation

@appleboy
Copy link
Copy Markdown
Member

Summary

Rewrites internal/templates/docs/*.md for developers integrating with AuthGate, not operators running it.

  • Split out all operator content (server setup, env vars, openssl / key generation, credentials file).
  • Make OIDC Discovery the integration starting point; document the actual Discovery response and its gotchas (unadvertised endpoints, conditional jwks_uri).
  • Document confidential-client paths (HTTP Basic / form body) alongside the public-client/PKCE path in the Authorization Code Flow.
  • Add nonce, state, and tighter PKCE guidance (S256-only, plain rejected).
  • Three new pages:
    • OpenID Connect — ID token claims, aud/nonce validation, /oauth/userinfo, how access-token verification differs.
    • Tokens & Revocation — refresh mechanics with the rotation-mode reuse-detection gotcha, /oauth/revoke, /oauth/introspect vs /oauth/tokeninfo, rate-limit defaults.
    • Errors — OAuth error catalog per grant type with integrator-side handling guidance.
  • Register the three new pages in the docs sidebar (internal/handlers/docs.go).
  • Add *.pem and *.key to .gitignore to prevent accidental JWT signing-key commits.
  • Fix mermaid v11 rendering (strip inline JSON bodies, as aliases, and em dashes that broke the sequenceDiagram parser).

Accuracy notes: all flow/claim/endpoint descriptions are cross-checked against the handlers and services (redirect-URI exact match, PKCE S256-only, form-urlencoded-only token endpoint, rate-limit defaults, ID-token claim set, UserInfo scope gating, etc.).

Test plan

  • make generate && make build succeeds
  • Navigate to /docs locally and verify all eight pages render and link correctly
  • Confirm every mermaid sequenceDiagram renders (client-credentials, auth-code-flow, device-flow, jwt-verification)
  • Verify sidebar order: Getting Started → Auth Code Flow → Device Flow → Client Credentials → OpenID Connect → JWT Verification → Tokens & Revocation → Errors
  • Spot-check a few in-page anchor links (e.g., tokens.md#rotation-mode-the-reuse-detection-gotcha, tokens.md#rate-limits)
  • Confirm jwt-private.pem (or any .pem/.key) is excluded from git status

🤖 Generated with Claude Code

- Split operator content out; focus on applications integrating with AuthGate
- Add OIDC Discovery entry point, supported scopes, and confidential-client paths
- Add new pages for OpenID Connect, Tokens and Revocation, and Errors
- Document nonce, PKCE rules, rotation reuse-detection, and rate-limit handling
- Register new doc pages in the sidebar
- Add *.pem and *.key to .gitignore to prevent signing-key leaks
- Fix mermaid v11 rendering by removing JSON bodies, aliases, and em dashes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 22, 2026 13:58
@appleboy appleboy merged commit 8dfd812 into main Apr 22, 2026
13 of 17 checks passed
@appleboy appleboy deleted the worktree-docs branch April 22, 2026 13:59
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR rewrites the embedded developer documentation (internal/templates/docs/*.md) to target integrators (client developers) rather than operators, making OIDC Discovery the primary entry point and expanding guidance on flows, token handling, and errors.

Changes:

  • Reframes “Getting Started” and per-flow docs around integration concerns (Discovery-first, PKCE/state/nonce guidance, confidential vs public client paths).
  • Adds three new integrator-focused pages: OpenID Connect, Tokens & Revocation, and Errors, and wires them into the docs sidebar.
  • Updates ancillary repo hygiene/docs rendering support (Mermaid adjustments) and adds *.pem / *.key to .gitignore to reduce accidental key commits.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
internal/templates/docs/tokens.md New page documenting refresh, rotation-mode behavior, revoke/introspect/tokeninfo, and rate limits for integrators
internal/templates/docs/oidc.md New page covering ID tokens vs access tokens, claim validation, and UserInfo usage
internal/templates/docs/errors.md New error catalog with scenario-based handling guidance
internal/templates/docs/jwt-verification.md Repositions JWT verification as access-token-only; adds pointers to online validation and OIDC
internal/templates/docs/getting-started.md Refocuses onboarding around Discovery + integration checklist and scope/grant guidance
internal/templates/docs/device-flow.md Updates Device Flow guidance and examples for integrators (polling interval, storage, rate limits)
internal/templates/docs/client-credentials.md Expands M2M guidance (auth methods, caching, scope restrictions, security checklist)
internal/templates/docs/auth-code-flow.md Adds client-type distinctions and tighter PKCE/state/nonce guidance plus confidential-client token exchange
internal/handlers/docs.go Registers new docs pages in the sidebar ordering
.gitignore Ignores *.pem and *.key to prevent accidental signing-key commits

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +112 to +115
| `scope` | recommended | Space-separated; include `openid` for an ID token |
| `state` | yes (CSRF) | Random value — validate on callback |
| `nonce` | OIDC | Required when `scope` contains `openid`; ends up in `id_token` for replay protection |
| `code_challenge` | PKCE | Derived as above |
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthGate does not require nonce for scope=openid requests (it is accepted and echoed into the ID token when provided, but validation does not enforce presence). The parameter table currently marks nonce as required for OIDC; please change this to recommended/strongly recommended to match server behavior.

Copilot uses AI. Check for mistakes.
-d "grant_type=refresh_token" \
-d "refresh_token=REFRESH_TOKEN" \
-d "client_id=YOUR_CLIENT_ID"
# Confidential clients: add -u "$CLIENT_ID:$CLIENT_SECRET" instead of client_id in body
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

POST /oauth/token with grant_type=refresh_token requires client_id in the form body; the handler does not accept HTTP Basic Auth as a replacement and will return invalid_request if client_id is omitted. Please update the example/comment so confidential clients still send client_id (and avoid implying client_secret/Basic auth is supported for refresh).

Suggested change
# Confidential clients: add -u "$CLIENT_ID:$CLIENT_SECRET" instead of client_id in body
# Confidential clients must still send client_id in the form body for refresh;
# do not replace it with -u "$CLIENT_ID:$CLIENT_SECRET" / HTTP Basic auth.

Copilot uses AI. Check for mistakes.
Comment on lines +59 to +65
curl -X POST https://your-authgate/oauth/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=REFRESH_TOKEN" \
-d "token_type_hint=refresh_token" \
-d "client_id=YOUR_CLIENT_ID"
# Confidential clients: include client_secret or use HTTP Basic
```
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

POST /oauth/revoke currently only requires the token form field; it does not read client_id/client_secret and does not perform client authentication. The docs/examples here imply client_id is required and confidential clients must authenticate—please revise to match the actual handler behavior (internal/handlers/token.go Revoke).

Copilot uses AI. Check for mistakes.
| Parameter | Required | Notes |
| ----------- | -------- | ---------------------------------------------------- |
| `client_id` | yes | Public client with Device Flow enabled |
| `scope` | no | Space-separated; defaults to `email profile` if omitted. Include `openid` for an ID token |
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Device Flow never includes an id_token in the /oauth/token response (TokenHandler.handleDeviceCodeGrant always returns buildTokenResponse(..., "") with empty id token). The docs currently imply openid is used to obtain an ID token here; please reword to reflect that openid may still be useful for UserInfo, but ID tokens are only issued via Authorization Code flow.

Suggested change
| `scope` | no | Space-separated; defaults to `email profile` if omitted. Include `openid` for an ID token |
| `scope` | no | Space-separated; defaults to `email profile` if omitted. Include `openid` if you need OIDC/UserInfo-compatible identity scopes; Device Flow does not return an `id_token` in the token response. ID tokens are only issued via Authorization Code flow. |

Copilot uses AI. Check for mistakes.
**A few gotchas with this document:**

### OAuth Clients
- `jwks_uri` and `id_token_signing_alg_values_supported` are **only present when AuthGate is configured for RS256/ES256** (asymmetric signing). On HS256 deployments they're omitted.
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Discovery endpoint omits jwks_uri for HS256 (no public keys), but it still includes id_token_signing_alg_values_supported when ID tokens are supported (it will be ["HS256"] in HS256 mode). The doc bullet currently says both fields are omitted on HS256 deployments; please adjust to match the actual Discovery response behavior (see internal/handlers/oidc.go).

Suggested change
- `jwks_uri` and `id_token_signing_alg_values_supported` are **only present when AuthGate is configured for RS256/ES256** (asymmetric signing). On HS256 deployments they're omitted.
- `jwks_uri` is **only present when AuthGate is configured for RS256/ES256** (asymmetric signing). On HS256 deployments it's omitted because there are no public keys. `id_token_signing_alg_values_supported` is still present when ID tokens are supported, and will be `["HS256"]` on HS256 deployments.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +17
| Token | Format | Lifetime (per client profile) | Used for |
| --------------- | ------------------- | ---------------------------------------------------- | ------------------------------------------------- |
| Access token | JWT | `short` 15m · `standard` 10h · `long` 24h (approx.) | `Authorization: Bearer` on API calls |
| Refresh token | JWT (treat as opaque) | `short` 1d · `standard` 30d · `long` 90d (approx.) | Exchanging for a new access token (`/oauth/token`) |
| ID token | JWT | Same as access token | Client-side identity — [see OIDC](./oidc) |

> Refresh tokens happen to be JWTs internally, but you should **treat them as opaque** — don't parse their claims in client code; you gain nothing and risk coupling to an internal detail.

The exact numbers depend on the per-client **token profile** set by the administrator. **Always trust `expires_in`** from the token response — never hardcode.
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ID token lifetime is not necessarily the same as the access token lifetime: ID tokens are generated via LocalTokenProvider.GenerateIDToken using the server's JWT_EXPIRATION default (no per-client token-profile TTL is passed), while access tokens can vary by client token profile (short/standard/long). Please update the table to avoid implying they always match.

Suggested change
| Token | Format | Lifetime (per client profile) | Used for |
| --------------- | ------------------- | ---------------------------------------------------- | ------------------------------------------------- |
| Access token | JWT | `short` 15m · `standard` 10h · `long` 24h (approx.) | `Authorization: Bearer` on API calls |
| Refresh token | JWT (treat as opaque) | `short` 1d · `standard` 30d · `long` 90d (approx.) | Exchanging for a new access token (`/oauth/token`) |
| ID token | JWT | Same as access token | Client-side identity — [see OIDC](./oidc) |
> Refresh tokens happen to be JWTs internally, but you should **treat them as opaque** — don't parse their claims in client code; you gain nothing and risk coupling to an internal detail.
The exact numbers depend on the per-client **token profile** set by the administrator. **Always trust `expires_in`** from the token response — never hardcode.
| Token | Format | Lifetime | Used for |
| --------------- | --------------------- | ---------------------------------------------------- | ------------------------------------------------- |
| Access token | JWT | Per client profile: `short` 15m · `standard` 10h · `long` 24h (approx.) | `Authorization: Bearer` on API calls |
| Refresh token | JWT (treat as opaque) | Per client profile: `short` 1d · `standard` 30d · `long` 90d (approx.) | Exchanging for a new access token (`/oauth/token`) |
| ID token | JWT | Server-configured; may differ from access token | Client-side identity — [see OIDC](./oidc) |
> Refresh tokens happen to be JWTs internally, but you should **treat them as opaque** — don't parse their claims in client code; you gain nothing and risk coupling to an internal detail.
Access-token and refresh-token lifetimes depend on the per-client **token profile** set by the administrator. ID-token lifetime is configured separately and may not match the access token. **Always trust the actual expiration returned in the token response or token claims** — never hardcode.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants