Secure JWT implementation patterns with access tokens, refresh tokens, and token rotation strategies
# JWT Token Management for Google Antigravity
Implement secure JWT authentication with Google Antigravity's Gemini 3 engine. This guide covers token generation, validation, refresh strategies, and security best practices for production applications.
## JWT Service Implementation
```typescript
// lib/jwt/service.ts
import jwt, { SignOptions, JwtPayload } from 'jsonwebtoken';
import { randomBytes } from 'crypto';
interface TokenPayload {
userId: string;
email: string;
roles: string[];
}
interface TokenPair {
accessToken: string;
refreshToken: string;
expiresIn: number;
}
const ACCESS_TOKEN_SECRET = process.env.JWT_ACCESS_SECRET!;
const REFRESH_TOKEN_SECRET = process.env.JWT_REFRESH_SECRET!;
const ACCESS_TOKEN_EXPIRY = '15m';
const REFRESH_TOKEN_EXPIRY = '7d';
export class JWTService {
generateTokenPair(payload: TokenPayload): TokenPair {
const accessToken = this.generateAccessToken(payload);
const refreshToken = this.generateRefreshToken(payload.userId);
return {
accessToken,
refreshToken,
expiresIn: 15 * 60, // 15 minutes in seconds
};
}
private generateAccessToken(payload: TokenPayload): string {
const options: SignOptions = {
expiresIn: ACCESS_TOKEN_EXPIRY,
algorithm: 'HS256',
issuer: 'antigravity-app',
audience: 'antigravity-users',
};
return jwt.sign(
{
sub: payload.userId,
email: payload.email,
roles: payload.roles,
type: 'access',
},
ACCESS_TOKEN_SECRET,
options
);
}
private generateRefreshToken(userId: string): string {
const tokenId = randomBytes(16).toString('hex');
const options: SignOptions = {
expiresIn: REFRESH_TOKEN_EXPIRY,
algorithm: 'HS256',
};
return jwt.sign(
{
sub: userId,
jti: tokenId,
type: 'refresh',
},
REFRESH_TOKEN_SECRET,
options
);
}
verifyAccessToken(token: string): TokenPayload | null {
try {
const decoded = jwt.verify(token, ACCESS_TOKEN_SECRET, {
algorithms: ['HS256'],
issuer: 'antigravity-app',
audience: 'antigravity-users',
}) as JwtPayload;
if (decoded.type !== 'access') {
return null;
}
return {
userId: decoded.sub as string,
email: decoded.email,
roles: decoded.roles,
};
} catch {
return null;
}
}
verifyRefreshToken(token: string): { userId: string; tokenId: string } | null {
try {
const decoded = jwt.verify(token, REFRESH_TOKEN_SECRET, {
algorithms: ['HS256'],
}) as JwtPayload;
if (decoded.type !== 'refresh') {
return null;
}
return {
userId: decoded.sub as string,
tokenId: decoded.jti as string,
};
} catch {
return null;
}
}
}
export const jwtService = new JWTService();
```
## Token Rotation with Blacklist
```typescript
// lib/jwt/token-store.ts
import { redis } from '@/lib/redis';
const REFRESH_TOKEN_PREFIX = 'refresh_token:';
const BLACKLIST_PREFIX = 'blacklist:';
export class TokenStore {
async storeRefreshToken(
userId: string,
tokenId: string,
expiresInSeconds: number
): Promise<void> {
const key = `${REFRESH_TOKEN_PREFIX}${userId}:${tokenId}`;
await redis.setex(key, expiresInSeconds, 'valid');
}
async isRefreshTokenValid(userId: string, tokenId: string): Promise<boolean> {
const key = `${REFRESH_TOKEN_PREFIX}${userId}:${tokenId}`;
const result = await redis.get(key);
return result === 'valid';
}
async revokeRefreshToken(userId: string, tokenId: string): Promise<void> {
const key = `${REFRESH_TOKEN_PREFIX}${userId}:${tokenId}`;
await redis.del(key);
}
async revokeAllUserTokens(userId: string): Promise<void> {
const pattern = `${REFRESH_TOKEN_PREFIX}${userId}:*`;
const keys = await redis.keys(pattern);
if (keys.length > 0) {
await redis.del(...keys);
}
}
async blacklistAccessToken(
token: string,
expiresInSeconds: number
): Promise<void> {
const key = `${BLACKLIST_PREFIX}${token}`;
await redis.setex(key, expiresInSeconds, 'blacklisted');
}
async isAccessTokenBlacklisted(token: string): Promise<boolean> {
const key = `${BLACKLIST_PREFIX}${token}`;
const result = await redis.get(key);
return result === 'blacklisted';
}
}
export const tokenStore = new TokenStore();
```
## Authentication Middleware
```typescript
// middleware/auth.ts
import { NextRequest, NextResponse } from 'next/server';
import { jwtService } from '@/lib/jwt/service';
import { tokenStore } from '@/lib/jwt/token-store';
export async function authMiddleware(request: NextRequest) {
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return NextResponse.json(
{ error: 'Missing authorization header' },
{ status: 401 }
);
}
const token = authHeader.substring(7);
// Check if token is blacklisted
if (await tokenStore.isAccessTokenBlacklisted(token)) {
return NextResponse.json(
{ error: 'Token has been revoked' },
{ status: 401 }
);
}
const payload = jwtService.verifyAccessToken(token);
if (!payload) {
return NextResponse.json(
{ error: 'Invalid or expired token' },
{ status: 401 }
);
}
// Add user info to request headers for downstream handlers
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-user-id', payload.userId);
requestHeaders.set('x-user-email', payload.email);
requestHeaders.set('x-user-roles', payload.roles.join(','));
return NextResponse.next({
request: { headers: requestHeaders },
});
}
```
## Refresh Token Endpoint
```typescript
// app/api/auth/refresh/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { jwtService } from '@/lib/jwt/service';
import { tokenStore } from '@/lib/jwt/token-store';
import { getUserById } from '@/lib/users';
export async function POST(request: NextRequest) {
const { refreshToken } = await request.json();
if (!refreshToken) {
return NextResponse.json(
{ error: 'Refresh token required' },
{ status: 400 }
);
}
const tokenData = jwtService.verifyRefreshToken(refreshToken);
if (!tokenData) {
return NextResponse.json(
{ error: 'Invalid refresh token' },
{ status: 401 }
);
}
const isValid = await tokenStore.isRefreshTokenValid(
tokenData.userId,
tokenData.tokenId
);
if (!isValid) {
return NextResponse.json(
{ error: 'Refresh token has been revoked' },
{ status: 401 }
);
}
// Revoke old refresh token (rotation)
await tokenStore.revokeRefreshToken(tokenData.userId, tokenData.tokenId);
const user = await getUserById(tokenData.userId);
if (!user) {
return NextResponse.json({ error: 'User not found' }, { status: 401 });
}
const newTokens = jwtService.generateTokenPair({
userId: user.id,
email: user.email,
roles: user.roles,
});
return NextResponse.json(newTokens);
}
```
## Best Practices
Google Antigravity's Gemini 3 engine recommends these JWT patterns: Use short-lived access tokens with longer-lived refresh tokens. Implement token rotation for refresh tokens to detect token theft. Blacklist access tokens on logout for immediate revocation. Store refresh tokens server-side for validation. Use strong secrets and rotate them periodically for security.This JWT prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their jwt implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For JWT projects, consider mentioning your framework version, coding style, and any specific libraries you're using.