Skip to main content
RapidDev - Software Development Agency
cursor-tutorial

How to Prevent Insecure Code from Cursor

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.

What you'll learn

  • How to create Cursor rules that enforce secure JWT handling
  • How to stop Cursor from storing tokens in localStorage
  • How to get Cursor to use proper signing algorithms and key rotation
  • How to prompt Cursor for secure authentication flows
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read10-15 minCursor Free+, any web project with authenticationMarch 2026RapidDev Engineering Team
TL;DR

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

1

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.

.cursor/rules/jwt-security.mdc
1---
2description: Secure JWT handling patterns
3globs: "*.ts,*.js,*auth*,*token*,*session*"
4alwaysApply: true
5---
6
7# JWT Security Rules
8
9## Token Storage:
10- NEVER store JWTs in localStorage or sessionStorage (XSS vulnerable)
11- ALWAYS use httpOnly, Secure, SameSite=Strict cookies for token storage
12- Access tokens: short-lived (15 min), in memory or httpOnly cookie
13- Refresh tokens: httpOnly cookie only, longer-lived (7 days)
14
15## Signing:
16- NEVER use HS256 with short or hardcoded secrets
17- PREFER RS256 or ES256 with key pairs
18- NEVER hardcode signing keys in source code
19- ALWAYS load keys from environment variables or secret manager
20
21## Validation:
22- ALWAYS verify token signature, expiration, issuer, and audience
23- ALWAYS check token is not on a revocation/blocklist
24- NEVER trust token payload without signature verification
25- Handle expired tokens gracefully with refresh flow
26
27## FORBIDDEN:
28```typescript
29localStorage.setItem('token', jwt); // NEVER
30const secret = 'my-secret-key'; // NEVER hardcode
31jwt.decode(token); // decode without verify = NEVER
32```

Expected result: Cursor generates secure JWT handling code with httpOnly cookies and proper validation.

2

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.

src/lib/auth-tokens.ts
1import jwt from 'jsonwebtoken';
2import { Response } from 'express';
3
4const ACCESS_TOKEN_EXPIRY = '15m';
5const REFRESH_TOKEN_EXPIRY = '7d';
6
7export 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};
20
21export 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};
29
30export 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.

3

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.

Cmd+L prompt
1@jwt-security.mdc @src/lib/auth-tokens.ts
2
3Create a login endpoint POST /api/auth/login that:
41. Validates email and password from request body with Zod
52. Looks up user by email in the database
63. Verifies password with bcrypt.compare
74. Generates access and refresh tokens using auth-tokens utility
85. 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 minute

Pro 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.

4

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.

Cmd+L prompt
1@jwt-security.mdc @codebase
2
3Audit this project for JWT security vulnerabilities. Check for:
41. Tokens stored in localStorage or sessionStorage
52. HS256 with short or hardcoded secrets
63. jwt.decode() used without jwt.verify()
74. Missing expiration checks on token validation
85. Tokens returned in response bodies (should be cookies only)
96. Missing httpOnly, Secure, or SameSite cookie flags
10
11For 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.

5

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.

Cmd+L prompt
1@jwt-security.mdc @src/lib/auth-tokens.ts
2
3Create 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 expiration
63. Checks the token is not on the revocation blocklist
74. Generates new access AND refresh tokens (rotation)
85. Adds the old refresh token to the blocklist
96. Sets new tokens as httpOnly cookies
107. Handles concurrent refresh requests safely (only one succeeds)
118. Returns 401 if refresh token is invalid or revoked

Expected result: Cursor generates a secure refresh endpoint with token rotation, revocation checking, and concurrency safety.

Complete working example

.cursor/rules/jwt-security.mdc
1---
2description: Secure JWT handling patterns
3globs: "*.ts,*.js,*auth*,*token*,*session*"
4alwaysApply: true
5---
6
7# JWT Security Rules
8
9## Token Storage:
10- NEVER store JWTs in localStorage or sessionStorage
11- ALWAYS use httpOnly, Secure, SameSite=Strict cookies
12- Access tokens: 15 min expiry, in httpOnly cookie
13- Refresh tokens: 7 day expiry, httpOnly cookie, restricted path
14
15## Signing:
16- ALWAYS use RS256 or ES256 with key pairs
17- NEVER hardcode signing keys in source code
18- Load keys from environment variables or secret manager
19- Support key rotation with kid (Key ID) header
20
21## Validation (ALWAYS):
22- Verify signature with public key
23- Check expiration (exp claim)
24- Verify issuer (iss claim)
25- Verify audience (aud claim)
26- Check revocation blocklist
27
28## FORBIDDEN Patterns:
29```typescript
30localStorage.setItem('token', jwt); // XSS vulnerable
31const secret = 'my-secret-key'; // Hardcoded secret
32jwt.decode(token); // No verification
33jwt.verify(token, secret, { algorithms: ['HS256'] }); // Weak algo
34res.json({ token: accessToken }); // Token in response body
35```
36
37## Required Pattern:
38```typescript
39import { 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.

ChatGPT Prompt

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).

Cursor Prompt

@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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.