Skip to content

feat(auth): shared Elysia auth middleware plugin factory (#47)#56

Merged
pranavp10 merged 1 commit into
mainfrom
auth-refactor-47-middleware-plugin
Jul 13, 2026
Merged

feat(auth): shared Elysia auth middleware plugin factory (#47)#56
pranavp10 merged 1 commit into
mainfrom
auth-refactor-47-middleware-plugin

Conversation

@pranavp10

@pranavp10 pranavp10 commented Jul 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds @reloop/auth/middlewarecreateAuthPlugin({ baseUrl, redis, ttl }) (default TTL 5s).
  • Macros: auth (fail-closed org), authNoOrg, apiKeyAuth, platformAdmin → canonical AuthContext.
  • Session cache keyed by session token + per-user index; always checks get-session status.
  • Package tests mount the plugin on a throwaway Elysia app (session, API key, cache, fail-closed, all four macros).
  • No service mounts the plugin yet (pilot is a later ticket).

Stacked on #55 (#46). Closes #47.

Test plan

  • bun test in packages/auth — 20 pass (helpers, isolation, middleware suite)
  • bun run typecheck for @reloop/auth
  • Characterization tripwire still green (apps/backend/auth)
  • CI green once stack base merges

Greptile Summary

This PR adds a shared Elysia auth middleware package entrypoint. The main changes are:

  • New @reloop/auth/middleware export with an auth plugin factory.
  • Session, API-key, no-org, and platform-admin macros returning AuthContext.
  • Session token cache keys and a per-user token index.
  • Middleware tests using a throwaway Elysia app and in-memory Redis.

Confidence Score: 4/5

The session cache path needs fixes before merging.

  • Cached sessions can continue authorizing after revocation or role/org changes.
  • Concurrent user-index writes can drop tokens and make later eviction incomplete.
  • The macro wiring and API-key paths are covered by focused package tests.

packages/auth/src/middleware/session.ts

Security Review

The new middleware sits on the auth boundary. Cached sessions can bypass revocation checks, and concurrent index updates can leave tokens out of bulk eviction.

Important Files Changed

Filename Overview
packages/auth/src/middleware/session.ts Adds session resolution and caching, with stale-session and lost-index risks in the new cache flow.
packages/auth/src/middleware/plugin.ts Adds the Elysia auth macros; the platform-admin context is narrower than the existing admin middleware.
packages/auth/src/middleware/keys.ts Adds session cookie parsing and cache key helpers for the new middleware.
packages/auth/src/apikey/validate.ts Narrows the API-key cache interface while remaining compatible with existing Redis-style callers.
packages/auth/package.json Adds the middleware export and Elysia dependency for the auth package.
packages/auth/test/middleware.plugin.test.ts Adds package tests for the new plugin macros and cache behavior.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
  participant Client
  participant Middleware as Auth middleware
  participant Redis
  participant Auth as get-session

  Client->>Middleware: Request with session cookie
  Middleware->>Redis: "GET session:token:<token>"
  alt cache hit
    Redis-->>Middleware: cached AuthContext
    Middleware-->>Client: Route runs with cached auth
  else cache miss
    Middleware->>Auth: GET /api/auth/v1/get-session
    Auth-->>Middleware: Current user/org/role
    Middleware->>Redis: "SET session:token:<token>"
    Middleware->>Redis: "Update session:user:<userId>"
    Middleware-->>Client: Route runs with fresh auth
  end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
  participant Client
  participant Middleware as Auth middleware
  participant Redis
  participant Auth as get-session

  Client->>Middleware: Request with session cookie
  Middleware->>Redis: "GET session:token:<token>"
  alt cache hit
    Redis-->>Middleware: cached AuthContext
    Middleware-->>Client: Route runs with cached auth
  else cache miss
    Middleware->>Auth: GET /api/auth/v1/get-session
    Auth-->>Middleware: Current user/org/role
    Middleware->>Redis: "SET session:token:<token>"
    Middleware->>Redis: "Update session:user:<userId>"
    Middleware-->>Client: Route runs with fresh auth
  end
Loading

Reviews (1): Last reviewed commit: "feat(auth): shared Elysia auth middlewar..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

Add @reloop/auth/middleware with createAuthPlugin({ baseUrl, redis, ttl })
registering auth, authNoOrg, apiKeyAuth, and platformAdmin macros that all
return the canonical AuthContext.

Session validation caches by session token (not cookie hash), maintains a
per-user token index for later central eviction, and always checks get-session
HTTP status. API-key path reuses absorbed validation. Package-level suite
mounts the plugin on a throwaway Elysia app — no service adoption yet.
Comment on lines +40 to +43
const cached = await opts.redis.get<AuthContext>(cacheKey);
if (cached) {
if (opts.requireOrg && !cached.organizationId) return null;
return cached;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Cached Sessions Skip Revocation

When a token is already cached, this path returns the cached AuthContext without calling get-session. A revoked session, removed active organization, or changed role can keep authorizing guarded routes until the Redis entry expires, even though the auth service would reject or downgrade the session.

Comment on lines +98 to +105
const indexKey = sessionUserIndexKey(userId);
const existing = (await redis.get<string[]>(indexKey)) ?? [];
if (existing.includes(token)) {
// Refresh TTL on the index entry.
await redis.set(indexKey, existing, ttl);
return;
}
await redis.set(indexKey, [...existing, token], ttl);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Concurrent Index Updates Lose Tokens

Two simultaneous logins for the same user can both read the same session:user:<userId> array and then write different appended arrays. The later write drops the other token from the index, so a later bulk eviction can miss that token and leave its session:token:<token> cache entry usable until TTL expiry.

Comment on lines +142 to +159
platformAdmin: {
async resolve({ status, request: { headers } }) {
const ctx = await resolveFromHeaders(headers, {
...base,
requireOrg: false,
allowApiKey: false,
requirePlatformAdmin: true,
});
if (!ctx) {
return status(401, {
message: "Unauthorized access",
why: "Platform admin privileges are required",
fix: "Sign in with a platform admin account",
});
}
return ctx;
},
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Platform Admin Context Shrinks

This shared platformAdmin macro returns only AuthContext, while the current admin middleware also supplies fields such as email, name, isPlatformAdmin, traceId, and logger. When admin routes migrate to this macro, handlers that read those fields can receive undefined and behave incorrectly at runtime.

Base automatically changed from auth-refactor-46-absorb-apikey to main July 13, 2026 05:12
@pranavp10 pranavp10 merged commit a1aaf88 into main Jul 13, 2026
1 check passed
@pranavp10 pranavp10 deleted the auth-refactor-47-middleware-plugin branch July 13, 2026 05:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auth refactor 4/10 — Unified middleware plugin factory + package test suite

1 participant