Fix Insecure Session Management in Vibe-Coded Apps
OverMCP Team
Quick answer
Insecure session management in vibe-coded apps often means missing or weak session expiration, storing tokens in vulnerable places (like localStorage), or failing to rotate session IDs after login. To fix it: use HTTP-only, secure, SameSite cookies for session tokens, set short expiration times (15-60 minutes), and regenerate session IDs on authentication state changes. Many AI-generated apps skip these steps, leaving user accounts exposed to session hijacking.
What to check first
Before diving into fixes, audit your app for these common session management gaps:
localStorage, sessionStorage, or document.cookie with HttpOnly=false. If you find tokens in localStorage, that's a red flag.Secure; HttpOnly; SameSite=Lax (or Strict).exp claim (if JWT) or check server-side session timeout. Anything over 24 hours is too long for most apps.If you're unsure, use a security headers checker to verify cookie attributes.
Step-by-step fix
Here's how to implement secure session management in a typical vibe-coded Next.js app using the App Router.
1. Use HTTP-only cookies instead of localStorage
AI-generated code often stores JWTs in localStorage for simplicity. This is vulnerable to XSS attacks. Move tokens to HTTP-only cookies.
Before (bad):
// AuthContext.js (vibe-coded)
localStorage.setItem('token', token);After (good):
// API route: /api/login
import { cookies } from 'next/headers';
export async function POST(request) {
const { username, password } = await request.json();
// authenticate user...
const token = generateJWT({ userId: user.id });
cookies().set('session_token', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 1, // 1 hour
});
return Response.json({ success: true });
}2. Set short expiration and rotate tokens
Set maxAge to 15-60 minutes for session tokens. Use a refresh token with longer expiry if needed, stored securely.
// Token generation (using jose library)
import { SignJWT } from 'jose';
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const token = await new SignJWT({ userId: user.id })
.setProtectedHeader({ alg: 'HS256' })
.setExpirationTime('15m') // short-lived
.setIssuedAt()
.sign(secret);3. Regenerate session ID on login/logout
Always issue a new token after authentication changes.
// On logout
cookies().set('session_token', '', { maxAge: 0 });
// Also invalidate server-side session if using database sessions4. Add logout endpoint that clears cookie
// /api/logout
export async function POST() {
cookies().set('session_token', '', { maxAge: 0 });
return Response.json({ success: true });
}5. Verify cookie attributes in production
Use your browser's dev tools to check the Set-Cookie header. It should look like:
Set-Cookie: session_token=abc123; Path=/; HttpOnly; Secure; SameSite=LaxFor a full audit, run a free AI app security scanner to catch other vulnerabilities.
Common mistakes
FAQ
What is session hijacking?
Session hijacking is when an attacker steals a user's session token (e.g., via XSS, network sniffing, or leaked cookies) and uses it to impersonate the user. Secure session management prevents this by using HTTP-only cookies, short expirations, and HTTPS.
How do I test if my session is secure?
Open your browser's developer tools, go to the Application tab, and check Cookies. Look for your session cookie. It should have HttpOnly, Secure, and SameSite flags. Also, log out and log in again to see if the token changes. Use a security headers checker to verify cookie attributes.
Can I use JWTs with HTTP-only cookies?
Yes, JWTs work well with HTTP-only cookies. The server sets the JWT as a cookie value. The client sends it automatically with each request. This combines the statelessness of JWTs with the security of HTTP-only cookies. Ensure the cookie has a short maxAge and SameSite=Lax.