Skip to content

Instantly share code, notes, and snippets.

@yeasin2002
Created June 29, 2025 18:21
Show Gist options
  • Select an option

  • Save yeasin2002/b42cae1c281cbb264b533f4e53c433da to your computer and use it in GitHub Desktop.

Select an option

Save yeasin2002/b42cae1c281cbb264b533f4e53c433da to your computer and use it in GitHub Desktop.
custom auth with next.js

Authentication Session Manager

A lightweight authentication session management system for Next.js applications using JWT tokens and HTTP-only cookies.

Overview

This module provides a complete session management solution that handles user authentication through encrypted JWT tokens stored in secure HTTP-only cookies. It's designed for Next.js applications and includes functions for login, logout, session retrieval, and automatic session refresh.

Features

  • JWT-based Authentication: Uses JSON Web Tokens with HS256 algorithm
  • Secure Cookie Storage: Sessions stored in HTTP-only cookies for enhanced security
  • Automatic Session Refresh: Built-in session renewal to prevent expiration
  • Simple API: Clean, easy-to-use functions for all authentication operations
  • TypeScript Support: Fully typed for better development experience

Dependencies

{
  "jose": "^4.x.x",
  "next": "^13.x.x || ^14.x.x"
}

Installation

npm install jose

Configuration

The module uses a hardcoded secret key for demonstration purposes. In production, you should:

  1. Use a strong, randomly generated secret key
  2. Store the secret in environment variables
  3. Consider using a longer session duration
// Replace this in production
const secretKey = process.env.JWT_SECRET || "your-strong-secret-key";

API Reference

encrypt(payload: any): Promise<string>

Encrypts a payload into a JWT token.

Parameters:

  • payload: The data to encrypt (typically user information and expiration)

Returns: A signed JWT token string

decrypt(input: string): Promise<any>

Decrypts and verifies a JWT token.

Parameters:

  • input: The JWT token string to decrypt

Returns: The decrypted payload

login(formData: FormData): Promise<void>

Handles user login by creating a session and setting a secure cookie.

Parameters:

  • formData: Form data containing user credentials (expects 'email' field)

Behavior:

  • Creates a user object with email and default name
  • Generates a session token with 10-second expiration
  • Sets an HTTP-only cookie with the session

logout(): Promise<void>

Logs out the user by clearing the session cookie.

Behavior:

  • Sets the session cookie to an empty value
  • Sets cookie expiration to a past date to remove it

getSession(): Promise<any | null>

Retrieves and decrypts the current session.

Returns:

  • Session data if valid session exists
  • null if no session or invalid session

updateSession(request: NextRequest): Promise<NextResponse | undefined>

Middleware function to refresh session expiration.

Parameters:

  • request: Next.js request object

Returns:

  • NextResponse with updated session cookie
  • undefined if no session exists

Behavior:

  • Extends session expiration by 10 seconds
  • Updates the session cookie with new expiration

Usage Examples

Basic Login Flow

import { login, getSession, logout } from './auth-session';

// Handle login form submission
async function handleLogin(formData) {
  await login(formData);
  // User is now logged in
}

// Check if user is authenticated
async function checkAuth() {
  const session = await getSession();
  if (session) {
    console.log('User:', session.user);
    return true;
  }
  return false;
}

// Logout user
async function handleLogout() {
  await logout();
  // User is now logged out
}

Middleware Usage

// middleware.ts
import { updateSession } from './lib/auth-session';
import { NextRequest } from 'next/server';

export async function middleware(request: NextRequest) {
  return await updateSession(request);
}

export const config = {
  matcher: [
    '/((?!api|_next/static|_next/image|.*\\.png$).*)',
  ],
};

Protected Route Example

// app/dashboard/page.tsx
import { getSession } from '@/lib/auth-session';
import { redirect } from 'next/navigation';

export default async function Dashboard() {
  const session = await getSession();
  
  if (!session) {
    redirect('/login');
  }

  return (
    <div>
      <h1>Welcome, {session.user.name}!</h1>
      <p>Email: {session.user.email}</p>
    </div>
  );
}

Security Considerations

⚠️ Important Security Notes:

  1. Change the Secret Key: The current implementation uses a hardcoded secret. In production:

    const secretKey = process.env.JWT_SECRET;
    if (!secretKey) throw new Error('JWT_SECRET environment variable is required');
  2. Extend Session Duration: The 10-second expiration is for demo purposes. Use appropriate durations:

    .setExpirationTime("24h") // or "7d", "30d", etc.
  3. Add CSRF Protection: Consider implementing CSRF tokens for additional security

  4. Use HTTPS: Always use HTTPS in production to protect cookie transmission

  5. Validate User Credentials: The current login function doesn't verify credentials

Session Structure

The session payload contains:

{
  user: {
    email: string,
    name: string
  },
  expires: Date,
  iat: number, // Issued at timestamp
  exp: number  // Expiration timestamp
}

Error Handling

The module will throw errors for:

  • Invalid JWT tokens
  • Expired tokens
  • Malformed session data

Implement proper error handling in your application:

try {
  const session = await getSession();
} catch (error) {
  console.error('Session error:', error);
  // Handle invalid session
}

License

This code is provided as-is for educational and development purposes.

Code:

import { SignJWT, jwtVerify } from "jose";
import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";

const secretKey = "secret";
const key = new TextEncoder().encode(secretKey);

export async function encrypt(payload: any) {
  return await new SignJWT(payload)
    .setProtectedHeader({ alg: "HS256" })
    .setIssuedAt()
    .setExpirationTime("10 sec from now")
    .sign(key);
}

export async function decrypt(input: string): Promise<any> {
  const { payload } = await jwtVerify(input, key, {
    algorithms: ["HS256"],
  });
  return payload;
}

export async function login(formData: FormData) {
  // Verify credentials && get the user

  const user = { email: formData.get("email"), name: "John" };

  // Create the session
  const expires = new Date(Date.now() + 10 * 1000);
  const session = await encrypt({ user, expires });

  // Save the session in a cookie
  cookies().set("session", session, { expires, httpOnly: true });
}

export async function logout() {
  // Destroy the session
  cookies().set("session", "", { expires: new Date(0) });
}

export async function getSession() {
  const session = cookies().get("session")?.value;
  if (!session) return null;
  return await decrypt(session);
}

export async function updateSession(request: NextRequest) {
  const session = request.cookies.get("session")?.value;
  if (!session) return;

  // Refresh the session so it doesn't expire
  const parsed = await decrypt(session);
  parsed.expires = new Date(Date.now() + 10 * 1000);
  const res = NextResponse.next();
  res.cookies.set({
    name: "session",
    value: await encrypt(parsed),
    httpOnly: true,
    expires: parsed.expires,
  });
  return res;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment