Releases: phlare/elixir-api-core
v0.4.1
Docs + dependabot-config slice. No functional or dependency changes since v0.4.0.
Docs
- Self-contained
.claude/instructions/so the template is usable standalone outside the parent workspace. Adds repo-localci_cd.md, points pre-existingcommit_workflowanddependabot_workflowreferences intra-repo, flags shared Elixir/Phoenix conventions as workspace-only. - Guidance parity refresh between
AGENTS.mdandCLAUDE.md.
Infra
- Dependabot schedule: weekly → daily, with grouped patch/minor bumps and individual majors.
open-pull-requests-limit5 → 10. Conventional-commit scope in titles.
v0.4.0 — Soft delete, GDPR purge, admin, and transactional email
-
Users and sole-owned accounts are now soft-deletable via deleted_at
-
Two-step deletion: soft_delete_user (user-facing, reversible) then
purge_user! (hard delete for GDPR right-to-erasure) -
Admin controller at /api/v1/admin/users with list/show/delete/restore/purge
-
system_admin role + RequireSystemAdmin plug
-
Daily CleanupDeletedUsersWorker auto-purges users soft-deleted past TTL
-
soft_delete_user preserves earlier account deleted_at timestamps as
provenance; restore_user only restores accounts co-deleted with the user
(exact deleted_at match) -
Provider-agnostic Swoosh plumbing: Local adapter in dev (mailbox viewer
on :4001), Test adapter in tests, commented production adapter
placeholder in runtime.exs. Downstream projects bring their own adapter -
Oban :email queue with a template+args worker pattern so jobs stay
JSON-serializable and the Swoosh.Email is rebuilt on perform -
Email verification flow:
- POST /api/v1/auth/verify_email (public, token in body)
- POST /api/v1/auth/send_verification (authenticated, rate-limited)
- Auth.register/1 auto-enqueues a verification email
- email_verified_at is recorded but does not gate login (downstream
projects decide what, if anything, to gate)
-
Password reset flow:
- POST /api/v1/auth/request_password_reset (public, rate-limited,
always 200 to prevent enumeration) - POST /api/v1/auth/reset_password (public)
- Reset tokens embed a password-hash fingerprint so replays after a
successful reset are rejected (one-shot without a nonce table)
- POST /api/v1/auth/request_password_reset (public, rate-limited,
-
Rate-limit buckets for reset normalize on trim+downcase so whitespace/
case variants share a bucket -
Requires APP_URL and FROM_EMAIL env vars in prod
-
verify_email is POST + JSON. Any client previously calling
GET /api/v1/auth/verify_email must switch to POST with {token} in the
body. The email link now targets APP_URL/verify-email?token=X (a
frontend page that collects the token and POSTs it to the API),
mirroring the reset-password pattern. -
APP_URL and FROM_EMAIL are required at boot in prod.
-
274 tests, 0 failures; Dialyzer clean
-
Includes: soft delete (#7e5f53f), email infra (#1550b5e), review fixes (#60bc026)
v0.3.2
v0.3.1 — CORS credentials + token TTL
Changes
- CORS credentials support —
credentials: truein CORS config (dev + prod) so browsers include HttpOnly cookies in cross-origin requests. Pairs with web-app-core v0.5.0's cookie-based refresh token transport. - Reduce refresh token TTL — 30 days → 7 days, limiting blast radius of compromised tokens.
- Remove CHANGELOG.md — in favor of GitHub releases.
Closes #22
v0.3.0 — Security Hardening
Security Hardening
All items from the 2026-03-16 security audit addressed:
- Security headers plug —
X-Content-Type-Options: nosniffandCache-Control: no-storeon all auth endpoints - Request body size limit — 1MB cap on
Plug.Parsers(down from default 8MB) - Password max length — 128-char limit to prevent bcrypt truncation and CPU exhaustion
- OAuth state validation — signed cookie stores state during
google_start, verified withsecure_compareon callback - CORS explicit origins — configurable per environment (dev: localhost, test: all, prod:
CORS_ALLOWED_ORIGINSenv var) - Rate limiting wired up — login (5/60s) and refresh (10/60s) endpoints enforce IP-based rate limits, return 429 +
Retry-After
Other fixes
- Bandit
read_timeoutmoved toruntime.exsso it isn't overridden localhost:5173(Vite) added to default CORS origins
Stats
- 159 tests, 0 failures
- 20 files changed, +354 / -27 lines
v0.2.2
What's new
- CORS support —
cors_plugadded to endpoint. Permissive in dev, configurable viaCORS_ALLOWED_ORIGINSenv var in prod (#10) - Dialyzer fix —
:ex_unitadded toplt_add_appsto resolve false positives on ExUnit internals in Elixir 1.19 + OTP 28 (#12) - Bandit timeout config — Dev
read_timeoutincreased to 5 min to prevent error-level log noise from idle browser connections (#11)
v0.2.1 — Bug Fix and Housekeeping
Changes
- Fix: logout no longer crashes when no refresh token is provided in request body or cookie
- Add GitHub Sponsors funding configuration
- Add web-app-core cross-links to README
- Remove
docs/PLATFORM_TEMPLATES.md(consolidated intotiny-inbox-product/docs/)
Full Changelog: v0.2...v0.2.1
v0.2
What's Changed
- CI fix: oasdiff breaking-change check now extracts base spec to workspace for Docker compatibility
- Dependabot: actions/checkout bumped from v4 to v6
- Docs: Added Related Templates section to README linking elixir-api-core and node-edge-core
No code changes — CI, docs, and dependency maintenance only.
v0.1.0
v0.1.0 — Initial Release
Multi-tenant identity, authentication, and authorization template for Phoenix APIs.
Highlights
10 endpoints — register, login, refresh, logout, switch account, Google OAuth (start/callback), /me, health, readiness
JWT access tokens (HS256, 15 min) + opaque refresh tokens (SHA-256 HMAC, 30 day, rotated)
Refresh rotation with reuse detection — replaying a revoked token revokes all user tokens
Multi-tenancy — accounts, users, memberships with role enforcement (owner | admin | member)
Owner invariant — row-level locking guarantees every account always has at least one owner
Google OAuth — configurable provider behaviour with three linking rules (login, link, create)
ETS-backed rate limiting for login and refresh endpoints
Oban background jobs — example worker + expired token cleanup
Fail-fast config validation — blocks unsafe dev secrets in production
OpenAPI 3.1 spec for all endpoints
Stack
Elixir 1.19.5 | OTP 28 | Phoenix 1.8 | PostgreSQL
Stats
122 tests, 0 failures | 7 DB tables | 5 core schemas | MIT licensed