POST /api/v1/auth/magic-link/request — sends one-time link; always 200 (no enumeration)
GET /api/v1/auth/magic-link/consume — validates + signs in (signup-on-first-link)
Wired into chi router; gated on userRepo implementing user.MagicLinkRepository
Config: auth.magic_link.{ttl,base_url} with sane defaults
11 unit tests vs fake repo / service / sender
Test plan
go test ./... — green (pkg/user/api +11 tests)
go vet ./... clean
## Summary
ADR-0028 Phase A.4 — passwordless-auth HTTP layer.
- POST /api/v1/auth/magic-link/request — sends one-time link; always 200 (no enumeration)
- GET /api/v1/auth/magic-link/consume — validates + signs in (signup-on-first-link)
- Wired into chi router; gated on userRepo implementing user.MagicLinkRepository
- Config: auth.magic_link.{ttl,base_url} with sane defaults
- 11 unit tests vs fake repo / service / sender
## Test plan
- go test ./... — green (pkg/user/api +11 tests)
- go vet ./... clean
Adds the two passwordless-auth endpoints behind /api/v1/auth/:
POST /magic-link/request — body {email}; always 200 (no enumeration leak)
GET /magic-link/consume — ?token=...; signs in (signup-on-first-link)
Sign-up flow: first consume for an unknown email creates the user with a
random unguessable bcrypt-hashed password — keeps the schema NOT NULL
constraint while permanently locking the password endpoints out.
Failure modes (missing/expired/already-consumed) collapse to a single
401 to prevent attackers distinguishing them. DB persist failures on
request silently degrade to the generic 200 to avoid leaking internal
state.
Config:
auth.magic_link.ttl (default 15m, env DLC_AUTH_MAGIC_LINK_TTL)
auth.magic_link.base_url (default http://localhost:8080)
Tests: 11 unit tests against fakes (repo, user service, sender) cover
happy path (new + existing user), normalization, bad JSON, persist
failure, missing/unknown/expired/consumed token, URL builder.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
ADR-0028 Phase A.4 — passwordless-auth HTTP layer.
Test plan
Adds the two passwordless-auth endpoints behind /api/v1/auth/: POST /magic-link/request — body {email}; always 200 (no enumeration leak) GET /magic-link/consume — ?token=...; signs in (signup-on-first-link) Sign-up flow: first consume for an unknown email creates the user with a random unguessable bcrypt-hashed password — keeps the schema NOT NULL constraint while permanently locking the password endpoints out. Failure modes (missing/expired/already-consumed) collapse to a single 401 to prevent attackers distinguishing them. DB persist failures on request silently degrade to the generic 200 to avoid leaking internal state. Config: auth.magic_link.ttl (default 15m, env DLC_AUTH_MAGIC_LINK_TTL) auth.magic_link.base_url (default http://localhost:8080) Tests: 11 unit tests against fakes (repo, user service, sender) cover happy path (new + existing user), normalization, bad JSON, persist failure, missing/unknown/expired/consumed token, URL builder.