Skip to content

Commit

Permalink
Split client and server sessions
Browse files Browse the repository at this point in the history
  • Loading branch information
delbaoliveira committed Mar 14, 2024
1 parent 6c61376 commit 8601e86
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 41 deletions.
4 changes: 2 additions & 2 deletions app/auth/01-auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file includes the authentication logic for
// signing up, logging in, and logging out using Server Actions.
// See `02-session.ts` for the session management logic.
// See `02-` for the session management logic.

// We're querying the database directly
// but at this point, we should recommend calling an Auth Provider's API.
Expand All @@ -14,7 +14,7 @@ import {
LoginFormSchema,
SignupFormSchema,
} from '@/app/auth/definitions';
import { createSession, deleteSession } from '@/app/auth/02-session';
import { createSession, deleteSession } from '@/app/auth/02-client-session';
import bcrypt from 'bcrypt';
import { eq } from 'drizzle-orm';
import { redirect } from 'next/navigation';
Expand Down
53 changes: 53 additions & 0 deletions app/auth/02-client-session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Option 1 (this file): Client-side stateless session with cookies | Optimistic auth check
// Option 2: Server-side sessions with tokens (or session ID) stored in a database | Secure auth check

// This file goes through **client-side session** management
// See `middleware.ts` and `03-dal.ts` for the authorization / data access logic

// Recommend iron-session and jose

import 'server-only';

import jwt from 'jsonwebtoken';
import { cookies } from 'next/headers';

// TODO: Replace with secret key from environment variables
const secretKey = 'yourSecretKey';

export async function createSession(id: number) {
const token = jwt.sign({ id }, secretKey, {
expiresIn: '1h',
});
const expiresAt = new Date(Date.now() + 60 * 60 * 1000);

cookies().set('token', token, {
httpOnly: true,
secure: true,
expires: expiresAt,
sameSite: 'lax',
path: '/',
});
}

// If invoking this function from:
// - Middleware, pass token from the request header
// - Server Actions or Server Components, use `cookies()`
// - Route handler, can use either headers or cookies

export async function verifyClientSession(token: string | undefined) {
if (!token) return null;

try {
const { id } = jwt.verify(token, secretKey);
if (!id) return null;
return { isAuth: true, userId: id };
} catch (error) {
return null;
}
}

export function updateSession() {}

export function deleteSession() {
cookies().delete('token');
}
60 changes: 21 additions & 39 deletions app/auth/02-session.ts → app/auth/02-server-session.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// This file includes the session management logic
// It's the 2nd part of the auth process
// See `01-auth.ts` for the authentication logic
// Option 1: Client-side stateless session with cookies | Optimistic auth check
// Option 2 (this file): Server-side sessions with tokens (or session ID) stored in a database | Secure auth check

// This file goes through **servers-side session** management
// See `middleware.ts` and `03-dal.ts` for the authorization / data access logic

import 'server-only';
Expand All @@ -14,58 +15,41 @@ import { cookies } from 'next/headers';
// TODO: Replace with secret key from environment variables
const secretKey = 'yourSecretKey';

// Option 1: Client-side stateless session with cookies | Optimistic auth check
// Option 2: Server-side sessions with tokens (or session ID) stored in a database | Secure auth check

export async function createSession(id: number) {
const token = jwt.sign({ id }, secretKey, {
expiresIn: '1h',
});
const expiresAt = new Date(Date.now() + 60 * 60 * 1000);

// Option 1: Send cookie from server to client
cookies().set('token', token, {
httpOnly: true,
secure: true,
expires: expiresAt,
sameSite: 'lax',
path: '/',
});

// Option 2: Store token in database
// 1. Store token (or session ID) in database
const data = await db
.insert(sessions)
.values({
userId: id,
token, // or session ID
token,
expiresAt,
})
.returning({ token: sessions.token });

// Store session ID in a cookie...
}

// Option 1: Optimistically check for token in cookies
export async function verifyClientSession() {
const token = cookies().get('token')?.value;
if (!token) return null;

try {
const { id } = jwt.verify(token, secretKey) as { id: number };
if (!id) return null;
return { isAuth: true, userId: id };
} catch (error) {
return null;
}
// 2. Set token in cookies to be read in middleware
cookies().set('token', token, {
httpOnly: true,
secure: true,
expires: expiresAt,
sameSite: 'lax',
path: '/',
});
}

// Option 2: Securely check for token in the database
export async function verifyServerSession() {
const token = cookies().get('token')?.value;
// If invoking this function from:
// - Middleware, pass token from the request header
// - Server Actions or Server Components, use `cookies()`
// - Route handler, can use either headers or cookies

export async function verifyServerSession(token: string | undefined) {
// const token = cookies().get('token')?.value;
if (!token) return null;

// This is nuts
try {
const data = await db
.select({
Expand All @@ -85,8 +69,6 @@ export async function verifyServerSession() {
export function updateSession() {}

export function deleteSession() {
// Option 1
cookies().delete('token');

// Option 2
// TODO: Delete token from database
}

0 comments on commit 8601e86

Please sign in to comment.