69 lines
4.1 KiB
Markdown
69 lines
4.1 KiB
Markdown
## Context
|
|
|
|
The existing repository now includes core database infrastructure and user models, but authentication is not implemented yet. The target behavior is defined by `openspec/specs/auth-oauth/spec.md` and refined through approved decisions: Authentik OIDC as the identity provider, strict production cookies, internal JWT access tokens, and server-side refresh token storage with revocation.
|
|
|
|
The backend is Python-based with async SQLAlchemy and Alembic. This change must integrate with that stack while keeping trust boundaries explicit and supporting predictable local development.
|
|
|
|
## Goals / Non-Goals
|
|
|
|
**Goals:**
|
|
- Implement Authentik OAuth2/OIDC login and callback flow.
|
|
- Validate provider tokens via JWKS before creating local session credentials.
|
|
- Mint internal short-lived JWT access tokens for API authorization.
|
|
- Persist hashed opaque refresh tokens in DB with rotation and revocation.
|
|
- Provide explicit logout that invalidates refresh state and clears cookies.
|
|
- Enforce environment-aware cookie policy (strict in production, relaxed on localhost).
|
|
|
|
**Non-Goals:**
|
|
- RBAC policy engine and permission modeling.
|
|
- Multi-device session management UX.
|
|
- Additional social identity providers.
|
|
|
|
## Decisions
|
|
|
|
1. **Use OIDC code flow + Authentik JWKS validation before local minting**
|
|
- Rationale: prevents blind trust in token exchange payloads and centralizes signature/claim checks (`iss`, `aud`, `exp`).
|
|
- Alternative considered: trust exchange response without independent validation. Rejected due to weaker security posture.
|
|
|
|
2. **Issue internal JWT access tokens instead of forwarding provider access tokens**
|
|
- Rationale: stable internal contract, decoupled claim shape, simpler downstream authorization.
|
|
- Alternative considered: pass-through provider tokens. Rejected due to tighter coupling and reduced control over TTL/claims.
|
|
|
|
3. **Use DB-backed opaque refresh tokens with hash-at-rest + rotation**
|
|
- Rationale: supports immediate revocation on logout and tighter reuse detection.
|
|
- Alternative considered: stateless long-lived JWT refresh tokens. Rejected because revocation and replay handling are weaker.
|
|
|
|
4. **Environment-aware cookie policy with secure defaults**
|
|
- Production: `Secure=true`, `SameSite=strict`, `HttpOnly=true`.
|
|
- Local dev: `Secure=false`, `SameSite=lax`, `HttpOnly=true`.
|
|
- Rationale: preserves security in production while enabling localhost development without TLS.
|
|
|
|
5. **Add dedicated refresh token persistence model and migration**
|
|
- Table fields: `id`, `user_id`, `token_hash`, `expires_at`, `created_at`, `revoked_at`, `user_agent`, `ip_address`.
|
|
- Indexes: unique `token_hash`, plus `user_id` and `expires_at` indexes.
|
|
|
|
## Risks / Trade-offs
|
|
|
|
- **[JWKS endpoint/network failures]** -> Cache JWKS keys with bounded TTL and fail closed with `401` on unverifiable tokens.
|
|
- **[Clock skew affecting token validity]** -> Allow small validation leeway and keep server clock synchronized.
|
|
- **[Refresh token replay attempts]** -> Rotate per refresh, revoke reused chains, and force re-authentication.
|
|
- **[Cookie behavior differences across browsers/environments]** -> Centralize cookie option builder and test dev/prod permutations.
|
|
- **[Added implementation surface area]** -> Keep modules focused (provider client, JWT service, refresh store, routes) and maintain high test coverage.
|
|
|
|
## Migration Plan
|
|
|
|
1. Add new refresh token model and Alembic migration.
|
|
2. Add configuration for OIDC endpoints/client credentials/JWT secret/cookie mode.
|
|
3. Implement auth services (provider exchange + JWKS verification, JWT mint/verify, refresh store).
|
|
4. Implement routes: `/auth/login`, `/auth/callback`, `/auth/refresh`, `/auth/logout`, `/auth/me`.
|
|
5. Add/expand tests (unit + integration) for auth and token lifecycle.
|
|
6. Verify quality gates (`pytest`, `ruff`, `mypy`) and migration status.
|
|
|
|
Rollback:
|
|
- Revert route/service changes and run Alembic downgrade for refresh-token migration if deployment requires rollback.
|
|
|
|
## Open Questions
|
|
|
|
- None blocking for initial implementation.
|
|
- Optional follow-up: enforce single active refresh token per user-agent/device (currently out of scope).
|