Skip to content

Add refresh tokens + revocation to Auth (#164)#178

Merged
gregwinn merged 1 commit into
developfrom
feature/auth-refresh-tokens
Jun 11, 2026
Merged

Add refresh tokens + revocation to Auth (#164)#178
gregwinn merged 1 commit into
developfrom
feature/auth-refresh-tokens

Conversation

@gregwinn

Copy link
Copy Markdown
Owner

Summary

Adds refresh tokens with rotation + revocation to the Auth module — sub-issue C (#164) of the authentication epic (#161), building directly on #163. Branches off develop (which now has #163 via #177), so no stacking issues this time.

match Auth.login(email, password)
  ok r => r   # %{user, access_token, refresh_token}
end
Auth.refresh(refresh_token)  # -> %{access_token, refresh_token} (rotated) | {:error, :invalid_token}
Auth.logout(refresh_token)   # -> :ok (idempotent)

Design

  • login/2 now returns %{user, access_token, refresh_token}.
  • refresh/1 looks the token up by hash, and if unexpired rotates it (old row deleted = single-use), returning a fresh access+refresh pair. Expired / unknown / already-rotated → :invalid_token.
  • logout/1 deletes the token row; idempotent.
  • Refresh tokens are opaque 32-byte random values; only the SHA-256 hash is stored in an auth_tokens table, so a DB leak doesn't expose live sessions. (SHA-256, not PBKDF2 — the token is already high-entropy, and a fast hash keeps lookup a single indexed query.)
  • Revocation = row deletion, which works cleanly now that Repo.insert/get/update return misaligned maps — row_to_map ignores DB column order (drops a field, no id) #172 makes Repo records carry id.
  • New config: auth.token_schema (default auth_token), auth.refresh_token_ttl (default 30d).

Testing

  • 6 new tests: login-returns-refresh, rotate-and-invalidate-old, logout-revokes (+ idempotent), expired-token, unknown-token, and an e2e codegen-resolution test for Auth.refresh. 18/18 auth tests pass.
  • Fake repo gained delete/1 and a monotonic id counter (so rotation can't hit an id collision).
  • Full suite: 740 tests, only the pre-existing winn_sqlite_tests esqlite-NIF environmental failure.

Docs

  • docs/modules.md: AuthToken schema + auth_tokens migration, Auth.refresh/Auth.logout sections, updated router + JS example (silent-refresh-on-401, logout).
  • docs/stdlib.md + CHANGELOG updated.

Part of #161. Closes #164 once merged.

🤖 Generated with Claude Code

Builds on the Auth module (#163). login now issues a long-lived refresh token
alongside the short-lived access JWT; adds Auth.refresh/1 and Auth.logout/1.
Part of the authentication epic (#161).

- login/2 returns %{user, access_token, refresh_token}.
- refresh/1: validates by token hash, rotates (presented token is single-use),
  returns a fresh %{access_token, refresh_token}; expired/unknown/rotated -> invalid_token.
- logout/1: deletes the token row (idempotent -> ok).
- Refresh tokens are opaque high-entropy random strings; only the SHA-256 hash is
  stored in an auth_tokens table, so a DB leak doesn't expose live sessions.
  Revocation = row deletion (works now that #172 makes records carry id).
- New config: auth.token_schema (default auth_token), auth.refresh_token_ttl
  (default 30d). Documents the AuthToken schema + auth_tokens migration.
- Tests: fake repo gains delete/1 + a non-reusing id counter; 6 new tests
  (login returns refresh, rotate+invalidate-old, logout revokes, expired,
  unknown, and an e2e resolution test). 18/18 auth tests; full suite 740 (only
  the pre-existing esqlite-NIF failure).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gregwinn gregwinn added this to the v1.0.0 — The Winn Platform milestone Jun 11, 2026
@gregwinn gregwinn added enhancement New feature or request area/runtime Runtime modules (winn_runtime.erl, stdlib) labels Jun 11, 2026
@gregwinn gregwinn merged commit 60dd267 into develop Jun 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/runtime Runtime modules (winn_runtime.erl, stdlib) enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant