Cursor often generates JWT code that stores tokens in localStorage, uses weak signing algorithms, skips expiration checks, or hardcodes secrets. By adding .cursor/rules/ with JWT security best practices, providing a secure auth utility for Cursor to reference, and prompting with explicit security requirements, you ensure Cursor generates code that handles JWTs safely with httpOnly cookies, RS256 signing, and proper validation.
Preventing insecure code from Cursor
JWT handling is one of the most security-sensitive patterns in web development, and AI-generated code frequently gets it wrong. Common issues include storing tokens in localStorage (vulnerable to XSS), using HS256 with weak secrets, missing token expiration validation, and hardcoding signing keys. This tutorial configures Cursor to generate secure authentication code by default.
Prerequisites
- Cursor installed with a web application project
- Basic understanding of JWT authentication flow
- A signing key or certificate available for RS256
- Familiarity with httpOnly cookies and CSRF protection
Step-by-step guide
Create a JWT security rule for Cursor
Create a JWT security rule for Cursor
Add a project rule that specifies secure JWT patterns and explicitly bans common insecure practices. Include both forbidden and required patterns so Cursor has clear boundaries for security-critical code.
1---2description: Secure JWT handling patterns3globs: "*.ts,*.js,*auth*,*token*,*session*"4alwaysApply: true5---67# JWT Security Rules89## Token Storage:10- NEVER store JWTs in localStorage or sessionStorage (XSS vulnerable)11- ALWAYS use httpOnly, Secure, SameSite=Strict cookies for token storage12- Access tokens: short-lived (15 min), in memory or httpOnly cookie13- Refresh tokens: httpOnly cookie only, longer-lived (7 days)1415## Signing:16- NEVER use HS256 with short or hardcoded secrets17- PREFER RS256 or ES256 with key pairs18- NEVER hardcode signing keys in source code19- ALWAYS load keys from environment variables or secret manager2021## Validation:22- ALWAYS verify token signature, expiration, issuer, and audience23- ALWAYS check token is not on a revocation/blocklist24- NEVER trust token payload without signature verification25- Handle expired tokens gracefully with refresh flow2627## FORBIDDEN:28```typescript29localStorage.setItem('token', jwt); // NEVER30const secret = 'my-secret-key'; // NEVER hardcode31jwt.decode(token); // decode without verify = NEVER32```Expected result: Cursor generates secure JWT handling code with httpOnly cookies and proper validation.
Create a secure auth utility for Cursor to import
Create a secure auth utility for Cursor to import
Build a secure token management module that Cursor can import instead of generating ad-hoc JWT code. This ensures consistent security patterns across all authentication-related code in your project.
1import jwt from 'jsonwebtoken';2import { Response } from 'express';34const ACCESS_TOKEN_EXPIRY = '15m';5const REFRESH_TOKEN_EXPIRY = '7d';67export const generateTokens = (userId: string, roles: string[]) => {8 const accessToken = jwt.sign(9 { sub: userId, roles, type: 'access' },10 process.env.JWT_PRIVATE_KEY!,11 { algorithm: 'RS256', expiresIn: ACCESS_TOKEN_EXPIRY, issuer: process.env.JWT_ISSUER }12 );13 const refreshToken = jwt.sign(14 { sub: userId, type: 'refresh' },15 process.env.JWT_PRIVATE_KEY!,16 { algorithm: 'RS256', expiresIn: REFRESH_TOKEN_EXPIRY, issuer: process.env.JWT_ISSUER }17 );18 return { accessToken, refreshToken };19};2021export const setTokenCookies = (res: Response, tokens: { accessToken: string; refreshToken: string }) => {22 res.cookie('access_token', tokens.accessToken, {23 httpOnly: true, secure: true, sameSite: 'strict', maxAge: 15 * 60 * 1000,24 });25 res.cookie('refresh_token', tokens.refreshToken, {26 httpOnly: true, secure: true, sameSite: 'strict', path: '/api/auth/refresh', maxAge: 7 * 24 * 60 * 60 * 1000,27 });28};2930export const verifyToken = (token: string): jwt.JwtPayload => {31 return jwt.verify(token, process.env.JWT_PUBLIC_KEY!, {32 algorithms: ['RS256'], issuer: process.env.JWT_ISSUER,33 }) as jwt.JwtPayload;34};Expected result: A secure auth utility that Cursor imports when generating authentication code.
Prompt Cursor for a secure login endpoint
Prompt Cursor for a secure login endpoint
When asking Cursor to generate auth endpoints, reference both the security rule and the auth utility. Be explicit about security requirements even if they are in the rules, because double reinforcement improves compliance.
1@jwt-security.mdc @src/lib/auth-tokens.ts23Create a login endpoint POST /api/auth/login that:41. Validates email and password from request body with Zod52. Looks up user by email in the database63. Verifies password with bcrypt.compare74. Generates access and refresh tokens using auth-tokens utility85. Sets tokens as httpOnly, Secure, SameSite cookies (NOT localStorage)96. Returns only the user profile (no tokens in response body)107. Handles invalid credentials with a generic error (no user enumeration)118. Rate limits to 5 attempts per IP per minutePro tip: Always say 'NOT localStorage' explicitly in auth prompts. Even with rules, Cursor sometimes falls back to localStorage for token storage because it is the most common pattern in its training data.
Expected result: Cursor generates a login endpoint that uses httpOnly cookies, RS256 tokens, and proper security practices.
Audit existing auth code for security issues
Audit existing auth code for security issues
Use Cursor Chat with @codebase to scan your project for insecure JWT patterns. This catches vulnerabilities in both AI-generated and manually written authentication code.
1@jwt-security.mdc @codebase23Audit this project for JWT security vulnerabilities. Check for:41. Tokens stored in localStorage or sessionStorage52. HS256 with short or hardcoded secrets63. jwt.decode() used without jwt.verify()74. Missing expiration checks on token validation85. Tokens returned in response bodies (should be cookies only)96. Missing httpOnly, Secure, or SameSite cookie flags1011For each vulnerability, show the file, the insecure code, and the fix.Expected result: Cursor identifies JWT security vulnerabilities across your codebase with specific fixes for each.
Generate a token refresh flow
Generate a token refresh flow
Token refresh is critical for security but complex to implement correctly. Prompt Cursor with explicit requirements for refresh token rotation, revocation, and concurrent request handling.
1@jwt-security.mdc @src/lib/auth-tokens.ts23Create a token refresh endpoint POST /api/auth/refresh that:41. Reads the refresh token from httpOnly cookie (not request body)52. Verifies the refresh token signature and expiration63. Checks the token is not on the revocation blocklist74. Generates new access AND refresh tokens (rotation)85. Adds the old refresh token to the blocklist96. Sets new tokens as httpOnly cookies107. Handles concurrent refresh requests safely (only one succeeds)118. Returns 401 if refresh token is invalid or revokedExpected result: Cursor generates a secure refresh endpoint with token rotation, revocation checking, and concurrency safety.
Complete working example
1---2description: Secure JWT handling patterns3globs: "*.ts,*.js,*auth*,*token*,*session*"4alwaysApply: true5---67# JWT Security Rules89## Token Storage:10- NEVER store JWTs in localStorage or sessionStorage11- ALWAYS use httpOnly, Secure, SameSite=Strict cookies12- Access tokens: 15 min expiry, in httpOnly cookie13- Refresh tokens: 7 day expiry, httpOnly cookie, restricted path1415## Signing:16- ALWAYS use RS256 or ES256 with key pairs17- NEVER hardcode signing keys in source code18- Load keys from environment variables or secret manager19- Support key rotation with kid (Key ID) header2021## Validation (ALWAYS):22- Verify signature with public key23- Check expiration (exp claim)24- Verify issuer (iss claim)25- Verify audience (aud claim)26- Check revocation blocklist2728## FORBIDDEN Patterns:29```typescript30localStorage.setItem('token', jwt); // XSS vulnerable31const secret = 'my-secret-key'; // Hardcoded secret32jwt.decode(token); // No verification33jwt.verify(token, secret, { algorithms: ['HS256'] }); // Weak algo34res.json({ token: accessToken }); // Token in response body35```3637## Required Pattern:38```typescript39import { generateTokens, setTokenCookies, verifyToken } from '@/lib/auth-tokens';40const tokens = generateTokens(user.id, user.roles);41setTokenCookies(res, tokens);42res.json({ user: { id: user.id, email: user.email } });43```Common mistakes when preventing Insecure Code from Cursor
Why it's a problem: Cursor stores JWT in localStorage by default
How to avoid: Add NEVER store JWTs in localStorage to your rules and always say NOT localStorage in auth prompts. Double reinforcement is essential for security patterns.
Why it's a problem: Using HS256 with a short string secret
How to avoid: Specify RS256 in your rules and provide the auth utility that uses RS256. Remove any HS256 examples from your codebase.
Why it's a problem: Returning tokens in JSON response body
How to avoid: Add to rules: NEVER return tokens in response body. ALWAYS set tokens as httpOnly cookies. Return only user profile data in the response.
Best practices
- Store JWTs in httpOnly, Secure, SameSite cookies — never localStorage
- Use RS256 or ES256 with key pairs instead of HS256 with shared secrets
- Set short access token expiry (15 minutes) with refresh token rotation
- Always verify signature, expiration, issuer, and audience on every request
- Implement a token revocation blocklist for logout and security events
- Create a shared auth utility file so Cursor imports it consistently
- Audit auth code with @codebase regularly since security issues are the most critical to catch
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Review this authentication code for security vulnerabilities. Check JWT storage (should be httpOnly cookies, not localStorage), signing algorithm (should be RS256), token validation (must verify signature, expiration, issuer), and refresh token flow (must rotate tokens).
@jwt-security.mdc @src/lib/auth-tokens.ts Create a secure logout endpoint that clears token cookies, adds the refresh token to the revocation blocklist, and returns 200. Also create a middleware that verifies the access token from the httpOnly cookie on every protected route.
Frequently asked questions
Is localStorage ever acceptable for JWT storage?
Only if your application has no XSS risk at all, which is effectively impossible for web apps. HttpOnly cookies are always the safer choice because JavaScript cannot access them.
What about storing tokens in memory for SPAs?
In-memory storage (React state or a module-level variable) is acceptable for access tokens in SPAs. The token is lost on page refresh, which is fine if you have a refresh token in an httpOnly cookie.
Should I use sessions instead of JWTs?
Server-side sessions with session IDs in httpOnly cookies are often simpler and more secure than JWTs. JWTs are better for stateless microservices. Consider your architecture before choosing.
How do I handle CSRF with httpOnly cookies?
Use SameSite=Strict cookies and add a CSRF token for state-changing requests. Include these requirements in your Cursor rules for auth endpoints.
Can Cursor generate OAuth2 flows?
Yes, but OAuth2 is complex. Provide your OAuth provider's documentation via @docs and reference existing auth utilities. Review generated OAuth code carefully since mistakes can expose user accounts.
Can RapidDev help secure our authentication system?
Yes. RapidDev conducts security reviews of authentication systems and helps teams implement secure JWT handling, OAuth2 flows, and session management with properly configured Cursor rules.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation