fix(auth): validate JWT server-side via getUser() in hooks.server.ts [P1.01]#117
fix(auth): validate JWT server-side via getUser() in hooks.server.ts [P1.01]#117cesarnml wants to merge 4 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThe pull request updates server-side session validation to add an Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Review Summary by QodoAdd server-side JWT validation in getSession hook
WalkthroughsDescription• Add server-side JWT validation via getUser() call • Verify token against Supabase auth server before accepting • Maintain backward compatibility with existing getSession() API • Document security rationale and implementation decisions Diagramflowchart LR
A["Client Request"] -->|JWT Token| B["getSession Hook"]
B -->|Call getUser| C["Validate vs Auth Server"]
C -->|Error or No User| D["Return null"]
C -->|Valid User| E["Call getSession"]
E -->|Return Session| F["Client Response"]
File Changes1. src/hooks.server.ts
|
Code Review by Qodo
1. Uncached getUser call fanout
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5fbc0bb. Configure here.
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| CodeStyle | 4 minor |
🟢 Metrics 0 complexity · 0 duplication
Metric Results Complexity 0 Duplication 0
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/hooks.server.ts (1)
39-52: ⚡ Quick winMemoize
getUser()to avoid redundant auth server round-trips per request.
getUser()contacts the Supabase Auth server on every call. Without memoization, a single SvelteKit request triggers multiplegetUser()network calls: once viagetProfile()at line 80 (which callsgetSession()with no pre-fetched session arg), and again from any routeload/actionhandler that callsgetSession()itself (e.g.,account/+page.server.ts). Sincehandlecreates a new closure scope per request, a simpleletcache is safe:⚡ Proposed memoization
event.locals.getSession = async () => { + let cachedSession: Session | null | undefined + return async () => { + if (cachedSession !== undefined) return cachedSession + const { data: { user }, error, } = await event.locals.supabase.auth.getUser() - if (error || !user) return null + if (error || !user) { + cachedSession = null + return null + } const { data: { session }, } = await event.locals.supabase.auth.getSession() + cachedSession = session return session + } }Alternatively, hoist the cache to the
handleclosure (same per-request lifetime, no function nesting):+ let sessionCache: Session | null | undefined event.locals.getSession = async () => { + if (sessionCache !== undefined) return sessionCache const { data: { user }, error, } = await event.locals.supabase.auth.getUser() - if (error || !user) return null + if (error || !user) { sessionCache = null; return null } const { data: { session }, } = await event.locals.supabase.auth.getSession() + sessionCache = session return session }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks.server.ts` around lines 39 - 52, The getSession implementation calls event.locals.supabase.auth.getUser() on every invocation causing duplicate network round-trips; introduce a per-request cache (e.g., let cachedUser / cachedSession inside the handle closure) and update event.locals.getSession to first return the cached value if present, otherwise call supabase.auth.getUser() once, store the result in the cache, then (if needed) call getSession() or return the cached session; reference event.locals.getSession, getUser(), getSession(), and getProfile() so callers reuse the cached user/session for the lifetime of the request.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/hooks.server.ts`:
- Around line 39-52: The getSession implementation calls
event.locals.supabase.auth.getUser() on every invocation causing duplicate
network round-trips; introduce a per-request cache (e.g., let cachedUser /
cachedSession inside the handle closure) and update event.locals.getSession to
first return the cached value if present, otherwise call supabase.auth.getUser()
once, store the result in the cache, then (if needed) call getSession() or
return the cached session; reference event.locals.getSession, getUser(),
getSession(), and getProfile() so callers reuse the cached user/session for the
lifetime of the request.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2c01f304-d640-4ff6-973a-afd76c093e8d
📒 Files selected for processing (2)
docs/02-delivery/phase-01/ticket-01-get-user.mdsrc/hooks.server.ts
5fbc0bb to
44fcd4f
Compare
07087c0 to
0a1118f
Compare
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Caches the getSession() result in a closure variable so repeated calls within the same request (getProfile, getProjects, layout load) reuse the validated JWT rather than making 4+ round-trips to Supabase Auth. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
0a1118f to
9bac13e
Compare
The wait-for-vercel-preview step exhausts retries because preview deployments are password-protected. Switched trigger to workflow_dispatch so the job no longer blocks PR CI. Fix path documented in roadmap: pass VERCEL_AUTOMATION_BYPASS_SECRET or make previews public. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Squash-merged to main via closeout-stack (P1.01). |

Summary
Review Triage
Cursor Bugbot + CodeRabbit (converging finding) — Patched ✅
Finding: `getSession()` is called 4× per request (handle → getProfile → layout.server.ts → getProjects), each invoking `getUser()` → Supabase Auth round-trip.
Fix: Memoize `getUser()` result in the `handle` closure via `cachedSession`. Subsequent calls within the same request return immediately.
Greptile — No actionable findings
No inline threads opened. Summary comment is noise.
Qodo — Same fanout finding + CI lockfile
CI lockfile issue resolved by rebasing onto main (lockfile updated in `d92746a`).
CI Baseline
`svelte-check` reports 66 pre-existing type errors across 41 files (unrelated to this PR). Test suite: 60/60 passing.
Vercel Build Failure
Pre-existing: `IP_INFO_TOKEN` missing from Vercel project env vars. Not introduced by this PR.
Note
Medium Risk
Changes request-time authentication validation by adding a
getUser()round-trip and memoizing the resulting session, which could affect auth behavior/performance and error handling for logged-in users.Overview
Auth/session validation is tightened in
hooks.server.ts.event.locals.getSession()now first callssupabase.auth.getUser()to verify the JWT with Supabase; on error or missing user it returnsnull, otherwise it fetches and returns the fullSessionviagetSession().Performance/behavior change: the computed session is memoized per request (
cachedSession) so repeatedgetSession()/getProfile()/getProjects()calls don’t trigger multiple Supabase auth round-trips. Documentation is updated to capture the security rationale and the compatibility-driven approach.Reviewed by Cursor Bugbot for commit 0a1118f. Bugbot is set up for automated code reviews on this repo. Configure here.