Authentication System
Overview
The dance-lessons-coach authentication system provides a passwordless magic-link flow as the primary mechanism, with legacy username+password support during the transition period. OpenID Connect (OIDC) integration is in progress for Phase B. See ADR-0028 for the migration strategy.
Authentication mechanisms supported
Username + password (legacy, ADR-0018)
- Endpoint:
POST /api/v1/auth/login
- Status: Operational, to be decommissioned in Phase C
- Details: bcrypt-hashed passwords, JWT token issuance
Magic link by email (ADR-0028 Phase A)
- Request endpoint:
POST /api/v1/auth/magic-link/request — accepts {email}, generates token, stores hash, sends email
- Consume endpoint:
GET /api/v1/auth/magic-link/consume?token=<...> — validates hash, marks consumed, issues JWT
- Always returns 200 on request to prevent email enumeration
- First-link sign-up: if email is unknown, consume endpoint creates the user record
OpenID Connect (ADR-0028 Phase B, work in progress)
- Status: Skeleton merged (
pkg/auth/), handlers and flow not yet wired
- Planned endpoints:
GET /api/v1/auth/oidc/start — generates state + PKCE, redirects to provider
GET /api/v1/auth/oidc/callback — exchanges code for tokens, validates id_token, issues internal JWT
- Provider config:
auth.oidc.providers.* in config
Magic-link flow detail
Configuration
Email (ADR-0029)
| Config key |
Env var |
Default |
Description |
auth.email.from |
DLC_AUTH_EMAIL_FROM |
noreply@dance-lessons-coach.local |
Sender address |
auth.email.smtp_host |
DLC_AUTH_EMAIL_SMTP_HOST |
localhost |
SMTP host |
auth.email.smtp_port |
DLC_AUTH_EMAIL_SMTP_PORT |
1025 |
SMTP port |
auth.email.smtp_use_tls |
DLC_AUTH_EMAIL_SMTP_USE_TLS |
false |
Use TLS |
auth.email.timeout |
DLC_AUTH_EMAIL_TIMEOUT |
10s |
Connection timeout |
Magic link (ADR-0028 Phase A)
| Config key |
Env var |
Default |
Description |
auth.magic_link.ttl |
DLC_AUTH_MAGIC_LINK_TTL |
15m |
Token lifetime |
auth.magic_link.base_url |
DLC_AUTH_MAGIC_LINK_BASE_URL |
http://localhost:8080 |
Base URL for links |
auth.magic_link.cleanup_interval |
DLC_AUTH_MAGIC_LINK_CLEANUP_INTERVAL |
1h |
Cleanup loop interval |
JWT (ADR-0021)
| Config key |
Env var |
Default |
Description |
auth.jwt.ttl |
DLC_AUTH_JWT_TTL |
1h |
Token time-to-live |
auth.jwt.secret_retention.retention_factor |
DLC_AUTH_JWT_SECRET_RETENTION_FACTOR |
2.0 |
Retention multiplier |
auth.jwt.secret_retention.max_retention |
DLC_AUTH_JWT_SECRET_MAX_RETENTION |
72h |
Maximum retention |
auth.jwt.secret_retention.cleanup_interval |
DLC_AUTH_JWT_SECRET_CLEANUP_INTERVAL |
1h |
Secret cleanup interval |
OIDC (Phase B, prep)
| Config key |
Env var |
Default |
Description |
auth.oidc.providers.<name>.issuer_url |
DLC_AUTH_OIDC_ISSUER_URL |
- |
Provider issuer URL |
auth.oidc.providers.<name>.client_id |
DLC_AUTH_OIDC_CLIENT_ID |
- |
Client ID |
auth.oidc.providers.<name>.client_secret |
DLC_AUTH_OIDC_CLIENT_SECRET |
- |
Client secret |
Token model
Magic-link tokens use SHA-256 hex hashing at rest — only the hash is stored in the database (token_hash column, 64 chars). The plaintext token is emailed to the user and must be supplied back to re-derive the hash. This means a database leak reveals no usable tokens. See pkg/user/magic_link.go for the rationale.
Cleanup loops
JWT secret retention (ADR-0021)
- Location:
pkg/user/jwt_manager.go — StartCleanupLoop
- Interval: Configurable via
auth.jwt.secret_retention.cleanup_interval (default: 1h)
- Behavior: Removes secrets older than retention period (TTL x retention_factor, capped at max_retention)
- Safety: Never removes the current primary secret
Magic-link expired tokens (ADR-0028 Phase A)
- Location:
pkg/user/magic_link_cleanup.go — StartCleanupLoop
- Interval: Configurable via
auth.magic_link.cleanup_interval (default: 1h)
- Behavior: Deletes tokens where
expires_at < now
- Implementation: Calls
DeleteExpiredMagicLinkTokens on the repository
Local dev setup
- Start services:
- Inspect emails: http://localhost:8025 (Mailpit UI)
- HTTPS for OIDC (Phase B):
See MKCERT.md for details.
Cross-references
Architecture Decision Records
| ADR |
Description |
| ADR-0018 |
Original username/password auth system |
| ADR-0021 |
JWT secret retention and cleanup |
| ADR-0028 |
Passwordless migration (Phase A complete, Phase B in progress) |
| ADR-0029 |
Email infrastructure (Mailpit) |
| ADR-0030 |
BDD parallel email assertions |
Documentation
Developer onboarding doc — see ADR-0028 for implementation details.