feat: JWT auth module, global guard, DEV_USER_ID removal - #8
Merged
Conversation
- Add argon2, @nestjs/jwt, @nestjs/passport auth dependencies - Migrate User table: add passwordHash, rename to users, drop name - AuthModule: AuthService (register/login with argon2), AuthController (POST /v1/auth/register, POST /v1/auth/login, both @public()) - JwtStrategy (passport-jwt), JwtAuthGuard (global APP_GUARD) - @public() and @currentuser() param decorators - Replace per-controller AuthGuard with global JwtAuthGuard - Remove AuthGuard from all module providers - All controllers use @currentuser() — DEV_USER_ID fully removed - Ingestion/retrieval services: userId now required, no fallback - query.controller.ts + query-stream: pass user.sub to retrieval - eval/seed.ts: local EVAL_USER_ID constant instead of importing constant - auth-guard.e2e-spec.ts: JWT-based test with route audit comment block - query.controller.spec.ts: updated for @currentuser() param signature - JWT_SECRET added to AppModule config validation (min 32 chars)
…ation tests
- Move env vars to module level before AppModule import so @nestjs/config v4
forRoot() validation passes with DATABASE_URL, JWT_SECRET, etc.
- Replace full AppModule with minimal TestIntegrationModule (PrismaModule +
RetrievalService + stub embedding provider) — avoids booting BullMQ, Auth,
Queues, and the real Gemini embedding API
- Add pool.on('error', ...) listener in PrismaService to suppress pg-pool's
unhandled 57P01 exceptions during container teardown
- Set ownership.integration env vars at module level too
@typescript-eslint/require-await flags embed() since it has no await. Switch to Promise.resolve() instead.
In Next.js 16, cookies() returns a Promise. The old code called
.get() directly on the unresolved Promise, which threw. The catch
block silently returned { token: null }, causing initClientToken()
to cache null. Every API call then sent no Authorization header,
got 401, and the 401 handler called /api/auth/logout + redirect —
making any newly-registered user appear to be immediately signed out.
Root cause: Next.js 16 client-side router.push() could lose the freshly-set auth_token cookie during navigation. Combined with a race where useQuery fires listDocuments() during render before useEffect-based token init completes, apiFetch sent requests without auth headers, triggering 401 → cookie clear → redirect to login. Changes: - window.location.href instead of router.push() after auth to force a full page navigation that carries the cookie - Module-level eager token fetch (before React render) so initPromise is set by the time useQuery fires during render - Shared initPromise so getAuthToken() awaits in-progress init instead of returning null
handleLogout only called setLoading(false) in the catch block. After a successful logout the button stayed stuck at "Logging out..." until the auth-check useEffect re-ran and hid the button entirely.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
argon2,@nestjs/jwt, and@nestjs/passportauth dependenciesUsertable — addpasswordHash, rename table tousers, dropnameAuthService(register/login with argon2 hashing) andAuthController(POST /v1/auth/register,POST /v1/auth/login, both tagged with@Public())JwtStrategy(passport-jwt) andJwtAuthGuardregistered as globalAPP_GUARD@Public()and@CurrentUser()parameter decoratorsAuthGuardwith globalJwtAuthGuardand removeAuthGuardfrom all module providers@CurrentUser();DEV_USER_IDis fully removeduserIdis now strictly required with no fallbackquery.controller.ts+query-stream: passuser.subdown to retrievaleval/seed.ts: switched to localEVAL_USER_IDconstant instead of importing deleted constantauth-guard.e2e-spec.ts: JWT-based e2e test with route audit comment blockquery.controller.spec.ts: updated for@CurrentUser()parameter signatureJWT_SECRETadded toAppModuleconfig validation schema (min 32 chars required)