-
Notifications
You must be signed in to change notification settings - Fork 0
fix(security): Implement JWKS-based JWT verification #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
CRITICAL SECURITY FIX: JWT signatures were not being verified, allowing attackers to forge tokens and access/delete any user's data. Root cause: Commit 6f41807 removed JWKS verification code assuming gateway verification was enabled, but verify_jwt=false was still set. Changes: - Add jose library for JWT verification - Implement JWKS-based signature verification for production - Validate issuer and audience claims - Fall back to decode-only for local dev (where JWKS is empty) - Remove incompatible deno.lock (version 5 not supported) Security model: - Production: Full JWKS signature verification (ES256/RS256) - Local dev: Decode-only (acceptable since DB is local) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
Security Review - PR #18: JWKS-based JWT VerificationSummaryThis PR addresses a critical security vulnerability where JWT signatures were not being verified, allowing attackers to forge authentication tokens. The implementation correctly restores JWKS-based verification while maintaining local development compatibility. ✅ Positive Aspects
🔴 Critical Issues1. Race Condition in JWKS Initialization (supabase/functions/shared/auth.ts:9-15)The singleton pattern is not thread-safe in concurrent environments: function getJwks() {
if (!_jwks && SUPABASE_URL) {
_jwks = createRemoteJWKSet(...) // Multiple threads could create this
}
return _jwks
}Risk: In high-concurrency scenarios, multiple requests could initialize Fix: Use double-checked locking or initialize once at module load: const _jwks = SUPABASE_URL
? createRemoteJWKSet(new URL(`${SUPABASE_URL}/auth/v1/.well-known/jwks.json`))
: null;2. Local Development Security Warning (supabase/functions/shared/auth.ts:65-68)The fallback to unverified decode is dangerous if the local development detection is bypassed. Recommendation: Add explicit logging when using unverified mode: if (isJwksError && isLocalDevelopment()) {
console.warn('[AUTH] Using unverified JWT decode for local development')
return decodeTokenWithoutVerification(token, ctx)
}3. Error Message Information Disclosure (supabase/functions/shared/auth.ts:71)throw new Error(`Unauthorized: ${errorMessage}`)Risk: Detailed error messages could leak information to attackers about the JWT verification system. Fix: Log detailed errors server-side but return generic message to client: console.error('[AUTH] JWT verification failed:', errorMessage)
throw new Error('Unauthorized')
|
| Category | Status | Notes |
|---|---|---|
| Authentication | ✅ Fixed | JWKS verification properly implemented |
| Authorization | Still relies on RLS policies (verify separately) | |
| Input Validation | ✅ Good | Claims validated, expiry checked |
| Error Handling | Information disclosure risk (issue #3) | |
| Cryptography | ✅ Correct | Using jose library, supports ES256/RS256 |
| Local Dev Security | Unverified tokens OK for local-only DB |
📝 Recommendations for Merging
Before merging to production:
- ✅ Fix critical issue Add user authentication checks to preference functions #1 (JWKS initialization race condition)
- ✅ Fix issue Preferencevalidator groq #3 (error message disclosure)
- ✅ Add comprehensive test coverage (issue Add SSE inventory+analysis stream and shared helpers #4)
- ✅ Add logging for local dev mode (issue Skip analyzer agent for no preferences #2)
⚠️ Consider issue feat(inventory): add Open Food Facts cache with Deno ingest script #5 (environment detection)⚠️ Validate SUPABASE_URL at startup (issue Feature/family #6)
Post-merge monitoring:
- Monitor for JWKS fetch failures
- Verify no unauthorized access in logs
- Check for increased latency in auth middleware
📚 Configuration Review
The config.toml correctly has:
[functions.ingredicheck]
verify_jwt = false
[functions.background]
verify_jwt = falseThis is appropriate since the application code now handles verification. However, document why gateway verification is disabled in comments.
Final Verdict
Status:
This PR fixes a critical security vulnerability and the overall approach is sound. However, critical issues #1-3 and test coverage (issue #4) must be addressed before merging to production.
The code quality is good and demonstrates understanding of JWT security best practices. The local development fallback is pragmatic and well-reasoned.
Great work on identifying and fixing this security issue! 🎉
Review conducted on commit 4752920
Fall back to decode-only mode for ANY JWKS verification error in local development, not just specific error messages. This handles network errors, fetch failures, and other edge cases that wouldn't match the previous substring checks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
Security Review - PR #18: JWT Verification ImplementationI've reviewed this critical security fix. Overall, this is an excellent security improvement that addresses a serious vulnerability. Here's my detailed analysis: ✅ Strengths
🔒 Security Concerns & RecommendationsHIGH PRIORITY - Local Development Security GapLocation: The fallback to unverified token decoding in local development creates a potential security risk: if (isLocalDevelopment()) {
return decodeTokenWithoutVerification(token, ctx)
}Issues:
Recommendation: Use an explicit environment variable flag: const IS_LOCAL_DEV = Deno.env.get('EDGE_FUNCTION_ENV') !== 'production'
if (IS_LOCAL_DEV) {
return decodeTokenWithoutVerification(token, ctx)
}Set this explicitly in your deployment configuration so it cannot be inferred or manipulated. MEDIUM PRIORITY - JWKS Error HandlingLocation: The current implementation falls back to unverified tokens for ANY JWKS error in local development. This is too broad. Issues:
Recommendation: Be more specific about which errors should trigger the fallback: } catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
if (IS_LOCAL_DEV && (
errorMessage.includes('no applicable key found') ||
errorMessage.includes('empty JWKS')
)) {
return decodeTokenWithoutVerification(token, ctx)
}
throw new Error(`Unauthorized: ${errorMessage}`)
}🐛 Potential IssuesToken Expiration Check ConsistencyLocation: The expiration check in 🔍 Code Quality
⚡ Performance Considerations
🧪 Test CoverageCRITICAL: The test plan shows only 1 of 3 items completed:
Recommendations:
📋 Configuration ReviewLocation: # Disable gateway JWT verification - our code handles auth
[functions.ingredicheck]
verify_jwt = falseThis comment is now accurate - your code does handle auth properly. However, consider:
🎯 Final RecommendationsBefore Merge:
After Merge:
📊 Overall AssessmentThis is a critical security fix that should be merged after addressing the high-priority concerns. The implementation is solid, but the local development fallback needs hardening to prevent potential bypass scenarios. Severity of Original Vulnerability: 🔴 CRITICAL - Complete authentication bypass Great work identifying and fixing this vulnerability! 🎉 |
Summary
joselibrary, matching the pattern used in the fly.io backendBackground
Commit
6f41807(Dec 14) removed working JWKS verification code based on the false assumption that the gateway verifies JWTs. However,verify_jwt = falsewas still set in config.toml, leaving authentication completely unverified. An attacker could forge tokens with anysubclaim and access/delete any user's data.Changes
supabase/functions/import_map.jsonjoselibrary for JWT verificationsupabase/functions/shared/auth.tsTest plan
🤖 Generated with Claude Code