Skip to content

feat(auth): add JWT token revocation with Redis blocklist#439

Open
antharya05 wants to merge 6 commits into
Dev-Card:mainfrom
antharya05:feat/jwt-token-revocation
Open

feat(auth): add JWT token revocation with Redis blocklist#439
antharya05 wants to merge 6 commits into
Dev-Card:mainfrom
antharya05:feat/jwt-token-revocation

Conversation

@antharya05
Copy link
Copy Markdown

@antharya05 antharya05 commented May 31, 2026

Summary

Implements secure JWT session invalidation using Redis blocklisting.

Previously, JWTs remained valid until expiration even after logout, meaning a stolen token could continue accessing protected APIs after a user logged out. This PR introduces server-side token revocation support so tokens are immediately invalidated after logout.

Closes #306


Type of Change

  • New feature
  • Security
  • Tests only

What Changed

  • Added protected DELETE /auth/logout endpoint for secure token revocation
  • Added Redis-backed JWT blocklist support with automatic TTL cleanup
  • Added shared JWT utility helpers in src/utils/jwt.ts
  • Extended global authenticate middleware to reject revoked tokens before jwtVerify()
  • Added logout/session revocation tests in src/__tests__/logout.test.ts
  • Preserved compatibility with existing OAuth/auth flows during rebase conflict resolution
  • Added hashed token signature storage instead of storing raw JWTs directly

How to Test

  1. Start Docker services and backend:
docker compose up -d
cd apps/backend
npx tsx src/server.ts
  1. Generate a JWT token:
$response = Invoke-RestMethod `
  -Method POST `
  -Uri http://localhost:3000/auth/dev-login `
  -ContentType "application/json" `
  -Body "{}"

$token = $response.token
  1. Verify authenticated access works:
Invoke-RestMethod `
  -Uri http://localhost:3000/api/profiles/me `
  -Headers @{ Authorization = "Bearer $token" }
  1. Logout and revoke the token:
Invoke-RestMethod `
  -Method DELETE `
  -Uri http://localhost:3000/auth/logout `
  -Headers @{ Authorization = "Bearer $token" }
  1. Retry the same request using the same token:
Invoke-RestMethod `
  -Uri http://localhost:3000/api/profiles/me `
  -Headers @{ Authorization = "Bearer $token" }

Expected result:

  • Request fails with 401 Unauthorized
  1. Run logout tests:
cd apps/backend
pnpm vitest run src/__tests__/logout.test.ts

Checklist

  • My code follows the project's coding style (pnpm -r run lint passes).
  • TypeScript compiles without errors (pnpm -r run typecheck).
  • I have added or updated tests for the changes I made.
  • All tests pass locally (pnpm -r run test).
  • I have updated documentation where necessary.
  • No new console.log or debug statements left in the code.
  • Breaking changes are documented in this PR description.

Screenshots / Recordings

Attached:

  • Successful authenticated request before logout
  • Successful logout response
  • Revoked token returning 401 Unauthorized
  • Passing logout test suite
Screenshot 2026-05-31 151441 Screenshot 2026-05-31 152147

Additional Context

  • Redis blocklist entries use TTL equal to remaining JWT lifetime, so entries self-expire automatically.
  • Redis stores hashed token signatures instead of raw JWTs for improved security.
  • Redis failures intentionally use a fail-open strategy to avoid taking down all authenticated requests during Redis outages.
  • Existing unrelated failing tests in event.test.ts were already present in the repository and are unrelated to this implementation.

@antharya05
Copy link
Copy Markdown
Author

hey @Harxhit , @ShantKhatri , please look into this PR, thank you.

@Harxhit Harxhit added the gssoc:approved Required label for every approved PR. Gives the base +50 points and enables contribution tracking. label Jun 1, 2026
Copy link
Copy Markdown
Collaborator

@Harxhit Harxhit left a comment

Choose a reason for hiding this comment

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

Also could please fix the linting issue that you have in all the files and paste the terminal proof on the PR description.

Comment thread apps/backend/src/__tests__/logout.test.ts
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

CI Results — ❌ Some checks failed

🖥️ Backend (❌ failure)

Check Status
Lint ❌ failure
Test ❌ failure
Typecheck ❌ failure

📱 Mobile (❌ failure)

Check Status
Lint ❌ failure
Test ❌ failure

🌐 Web (⏭️ skipped)

Check Status
Check ⚪ unknown
Build ⚪ unknown

🕐 Last updated: Tue, 02 Jun 2026 09:01:43 GMT

fix(ci): resolve mobile lint/test failures and improve auth coverage

fix(ci): restore selective CI script and stabilize app test env
@antharya05 antharya05 force-pushed the feat/jwt-token-revocation branch from cd1ddad to 16838b6 Compare June 2, 2026 07:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved Required label for every approved PR. Gives the base +50 points and enables contribution tracking.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issue Template: Insecure Session Invalidation via Redis JWT Blocklist

2 participants