feat BREAKING auth main #6 / 43
aaronrene · 5 days ago · Jun 3, 2026 · Diff

feat(auth): persistent login system + C7 session introspection

Implements the complete persistent login stack for the hosted gateway and self-hosted hub, plus the C7 session introspection contract for Scooling.

## Persistent login (refresh token rotation) - hub/lib/refresh-token-core.mjs: storage-agnostic refresh token core — issue, rotate (with reuse/family-revocation detection), revoke, prune. - hub/gateway/refresh-token-store.mjs: Netlify Blob-backed store (eventual consistency; strong is unavailable in Lambda-compat mode — using strong caused BlobsConsistencyError that silently blocked every cookie write). - hub/refresh-tokens.mjs: file-backed store for self-hosted. - hub/auth-session.mjs: HttpOnly cookie policy + Express handler factories for POST /api/v1/auth/refresh and POST /api/v1/auth/logout. - hub/gateway/server.mjs + hub/server.mjs: wired into both runtimes. - netlify/functions/gateway.mjs: provisions gateway-auth blob per-invocation (eventual consistency) and tears it down in finally. - web/hub/config.js: API base URL → api.knowtation.store for first-party cookie behavior (same registrable domain as www.knowtation.store). - web/hub/hub.js: client-side silent refresh on 401, server-side logout.

## C7 session introspection - GET /api/v1/auth/session: Bearer JWT → {sub, provider, id, name, role, iat, exp, scopes[]}. No DB call. Cross-origin safe (Bearer, no cookie). - scopesForRole: role-derived today; C4 explicit grants replaces derivation without changing the response shape — Scooling's contract is stable. - X-Powered-By: Express disabled globally (information leak fix). - docs/openapi.yaml + hub/gateway/README.md: full C7 + refresh + logout docs.

## Security / dependency hardening - npm audit fix in hub/bridge and hub/gateway (patched high CVE in tmp). - test/hub-note-outline-self-hosted-route.test.mjs: scope fix for brittle test.

## Tests (all 7 tiers, 100% pass) - test/refresh-token-core.test.mjs: 50 tests for the storage-agnostic core. - test/gateway-refresh-token-store.test.mjs: blob + file backend tests. - test/auth-refresh-wiring.test.mjs + test/auth-session.test.mjs: self-hosted. - test/gateway-auth-refresh-wiring.test.mjs: hosted gateway wiring. - test/gateway-session-introspection.test.mjs: 32 tests across all 7 tiers for C7 (unit → structural, integration → HTTP, e2e, stress × 50 concurrent, data-integrity, performance p99 < 100ms, security: wrong secret / tampered payload / alg:none / empty sub / query-bypass / no stack-trace leak).

sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2 sha
+76 ~6 symbols
707 changed · 707 in snapshot files
sha256:de940efa0cf44accecabfa6bfb6c3b701117812bd5d88f862239b4b6f82b2b60 snapshot
+76
symbols added
~6
symbols modified
707
files changed
707
files in snapshot
0
dead code introduced
Semantic Changes 82 symbols
~ hub/auth-session.mjs .mjs 9 symbols added
+ clearCookieOptions function function clearCookieOptions L55–58
+ createLogoutHandler function function createLogoutHandler L169–186
+ createRefreshHandler function function createRefreshHandler L126–156
+ issueRefreshCookie function async_function issueRefreshCookie L104–110
+ logoutHandler function async_function logoutHandler L171–185
+ readPresentedToken function function readPresentedToken L82–87
+ refreshCookieOptions function function refreshCookieOptions L34–47
+ refreshError function function refreshError L61–73
+ refreshHandler function async_function refreshHandler L129–155
~ hub/gateway/refresh-token-store.mjs .mjs 15 symbols added
+ createGatewayRefreshStore function function createGatewayRefreshStore L219–225
+ getBlobStore function function getBlobStore L74–76
+ issueRefreshToken function async_function issueRefreshToken L155–160
+ loadRefreshRecords function async_function loadRefreshRecords L131–134
+ normalizeRecords function function normalizeRecords L85–94
+ pruneRefreshTokens function async_function pruneRefreshTokens L207–212
+ readFromBlob function async_function readFromBlob L96–100
+ readFromFile function async_function readFromFile L107–116
+ refreshFilePath function function refreshFilePath L64–67
+ revokeAllRefreshTokensForSub function async_function revokeAllRefreshTokensForSub L195–200
+ revokeRefreshToken function async_function revokeRefreshToken L183–188
+ rotateRefreshToken function async_function rotateRefreshToken L169–176
+ saveRefreshRecords function async_function saveRefreshRecords L141–147
+ writeToBlob function async_function writeToBlob L102–105
+ writeToFile function async_function writeToFile L118–125
~ hub/lib/refresh-token-core.mjs .mjs 12 symbols added
+ cloneRecords function function cloneRecords L124–132
+ generateRefreshToken function function generateRefreshToken L98–102
+ hashSecret function function hashSecret L72–74
+ issueToken function function issueToken L142–169
+ parseToken function function parseToken L109–117
+ pruneExpired function function pruneExpired L331–348
+ revokeAllForSub function function revokeAllForSub L234–244
+ revokeFamily function function revokeFamily L194–203
+ revokeToken function function revokeToken L212–225
+ rotateToken function function rotateToken L259–319
+ safeEqualHashes function function safeEqualHashes L84–90
+ sanitizeMeta function function sanitizeMeta L177–184
~ hub/refresh-tokens.mjs .mjs 8 symbols added
+ filePathFor function function filePathFor L36–38
+ issueRefreshToken function function issueRefreshToken L89–94
+ pruneRefreshTokens function function pruneRefreshTokens L145–150
+ readRefreshTokens function function readRefreshTokens L47–65
+ revokeAllRefreshTokensForSub function function revokeAllRefreshTokensForSub L132–137
+ revokeRefreshToken function function revokeRefreshToken L119–124
+ rotateRefreshToken function function rotateRefreshToken L104–111
+ writeRefreshTokens function function writeRefreshTokens L72–80
~ test/auth-refresh-wiring.test.mjs .mjs 1 symbol added
+ load function function load L19–22
~ test/auth-session.test.mjs .mjs 10 symbols added
+ asyncSigner function function asyncSigner L189–189
+ clearCookie method method clearCookie L41–41
+ cookie method method cookie L40–40
+ cookieOptions function function cookieOptions L54–54
+ fileStore function function fileStore L46–52
+ issueAccessToken function function issueAccessToken L55–55
+ json method method json L39–39
+ mockRes function function mockRes L30–43
+ set method method set L37–37
+ status method method status L38–38
+ load function function load L20–23
+ createStubBlobStore function function createStubBlobStore L32–48
+ get method async_method get L37–42
+ setJSON method async_method setJSON L43–46
+ adminPayload function function adminPayload L52–60
+ createGateway function async_function createGateway L75–84
+ get function async_function get L86–90
+ makeJwt function function makeJwt L28–36
+ startServer function function startServer L62–73
+ validPayload function function validPayload L38–50
~ test/refresh-token-core.test.mjs .mjs 1 symbol added
+ buildLargeStore function function buildLargeStore L39–59
~ test/refresh-tokens-store.test.mjs .mjs 1 symbol added
+ storeFile function function storeFile L36–36
~ hub/gateway/README.md .md 2 symbols modified
~ hub/gateway/server.mjs .mjs 5 symbols added
+ decodeVerifiedToken function function decodeVerifiedToken L209–215
+ issueAccessTokenForSub function function issueAccessTokenForSub L239–249
+ issueRefreshCookieSafe function async_function issueRefreshCookieSafe L357–385
+ refreshCookiePolicy function function refreshCookiePolicy L341–348
+ scopesForRole function function scopesForRole L225–228
~ hub/server.mjs .mjs 3 symbols added, 1 symbol modified
+ issueAccessTokenForSub function function issueAccessTokenForSub L228–231
+ issueSession function function issueSession L464–475
+ refreshCookiePolicy function function refreshCookiePolicy L247–253
~ netlify/functions/gateway.mjs .mjs 1 symbol modified
~ web/hub/hub.js .js 1 symbol added, 1 symbol modified
+ refreshAccessToken function async_function refreshAccessToken L245–272
~ api
Files Changed
+707
707 in snapshot
+ .env.example .example
+ .gitignore .gitignore
+ .gitleaks.toml .toml
+ .museattributes .museattributes
+ .museignore .museignore
+ .nvmrc .nvmrc
+ AGENTS.md .md
+ README.md .md
+ backups/.gitignore .gitignore
+ cli/index.mjs .mjs
+ docs/SPEC.md .md
+ hub/roles.mjs .mjs
+ lib/air.mjs .mjs
+ lib/chunk.mjs .mjs
+ lib/vault.mjs .mjs
+ lib/write.mjs .mjs
+ netlify.toml .toml
+ package.json .json
+ public/.gitkeep .gitkeep
+ vault/meta/.gitkeep .gitkeep
+ web/index.html .html

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2 --body "your comment"