refactor(auth): migrate plain-session batch B to shared plugin (#51)#60
Conversation
Migrate template, api-key, and logs onto createAuthPlugin following the pilot recipe. Delete bespoke cookie-auth / api-key-auth middleware. - template: shared plugin + collabAuth (profile fields for websocket presence) - api-key / logs: straight plugin mount with fail-closed auth - Smoke tests per service; workflow has no auth middleware (skipped)
| const response = await fetch( | ||
| `${templateConfig.BASE_URL.replace(/\/$/, "")}/api/auth/v1/get-session`, | ||
| { | ||
| method: "GET", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| Cookie: cookie, | ||
| }, | ||
| }, | ||
| ); | ||
| if (!response.ok) throw AuthErrors.unauthorized(); | ||
|
|
||
| const body = (await response.json()) as { | ||
| user?: { | ||
| id: string; | ||
| email?: string; | ||
| name?: string; | ||
| image?: string; | ||
| activeOrganizationId?: string | null; | ||
| role?: string | null; |
There was a problem hiding this comment.
When get-session is unreachable or returns malformed JSON, this direct fetch() or response.json() throws out of collabAuth. The previous template auth path caught session validation failures and returned unauthorized, but this WebSocket guard can now turn an auth-service blip into an unhandled upgrade error instead of a clean rejection.
| /** Batch B migration: shared auth plugin (fail-closed org via `auth`). */ | ||
| export const authMiddleware = new Elysia({ name: "auth-middleware" }).use( | ||
| createAuthPlugin({ | ||
| baseUrl: apiKeyConfig.BASE_URL, | ||
| redis: sessionRedis, |
There was a problem hiding this comment.
This passes the session-scoped Redis cache into the shared auth plugin, and that same Redis object is used for API-key validation. If API-key revocation or eviction still targets the old API-key cache namespace, a key cached through this path is read back from reloop-session:apikey:v1:* and can keep authenticating until the API-key cache TTL expires.
| const result = await validateApiKey(apiKey, sessionRedis); | ||
| if (result?.organizationId) { | ||
| return { | ||
| userId: result.userId, | ||
| organizationId: result.organizationId, | ||
| role: null as string | null, | ||
| authType: "apikey" as const, | ||
| apiKeyId: result.apiKeyId, | ||
| userEmail: undefined as string | undefined, | ||
| userName: undefined as string | undefined, | ||
| userImage: undefined as string | undefined, | ||
| }; |
There was a problem hiding this comment.
collabAuth validates API keys against sessionRedis, so collaboration API-key lookups are cached in the session namespace rather than the old API-key namespace. If revocation cleanup deletes only the API-key namespace, a revoked collaboration key can still pass this branch from cache until the long API-key TTL expires.
Summary
Batch B plain-session migrations onto
@reloop/auth/middleware:collabAuthfor collab WS (email/name/image)@reloop/apikeyfor key generation helpersinsertAuthdroppedStacked on #59 (#50). Closes #51.
Test plan
tsc --noEmitgreen for all threeGreptile Summary
This PR migrates several plain-session backends onto the shared auth middleware. The main changes are:
collabAuthpath for collaboration WebSocket profile fields.Confidence Score: 4/5
The changed auth paths need fixes before merging.
apps/backend/template/src/middleware/auth.ts and apps/backend/api-key/src/middleware/auth.ts
Security Review
API-key validation is now routed through a session-scoped Redis cache in the migrated paths. If revocation cleanup still targets the old API-key namespace, revoked keys can remain usable from the new cache namespace until expiry.
Important Files Changed
createAuthPlugin, with a cache namespace change that can affect API-key revocation behavior.collabAuth; the direct session fetch can escape on auth-service failures and API-key validation uses the session cache namespace.collabAuthso presence fields are available.Reviews (1): Last reviewed commit: "refactor(auth): migrate plain-session ba..." | Re-trigger Greptile