🔥 Remove support for legacy sessions (#1654)

This commit is contained in:
Luke Vella 2025-03-31 15:54:21 +01:00 committed by GitHub
parent 0bdceb7bbe
commit 13e078cb88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 0 additions and 219 deletions

View file

@ -4,12 +4,6 @@ import NextAuth from "next-auth";
import { nextAuthConfig } from "@/next-auth.config"; import { nextAuthConfig } from "@/next-auth.config";
import {
deleteLegacyCookie,
getLegacySession,
migrateLegacyJWT,
} from "../legacy/next-auth-cookie-migration";
const { auth } = NextAuth(nextAuthConfig); const { auth } = NextAuth(nextAuthConfig);
export const withAuth = ( export const withAuth = (
@ -24,20 +18,6 @@ export const withAuth = (
console.error(e); console.error(e);
} }
let isLegacySession = false;
let isExpiredLegacySession = false;
if (!session) {
try {
session = await getLegacySession();
if (session) {
isLegacySession = true;
}
} catch {
isExpiredLegacySession = true;
}
}
try { try {
const res = await nextAuthConfig.callbacks.authorized({ const res = await nextAuthConfig.callbacks.authorized({
request, request,
@ -55,20 +35,6 @@ export const withAuth = (
const middlewareRes = await middleware(request); const middlewareRes = await middleware(request);
if (isLegacySession) {
console.warn("Found legacy session, migrating…");
try {
await migrateLegacyJWT(middlewareRes);
} catch (e) {
console.error(e);
}
}
if (isExpiredLegacySession) {
console.warn("Found expired legacy session, deleting…");
deleteLegacyCookie(middlewareRes);
}
return middlewareRes; return middlewareRes;
}; };
}; };

View file

@ -1,30 +0,0 @@
import hkdf from "@panva/hkdf";
import { jwtDecrypt } from "jose";
import type { JWT } from "next-auth/jwt";
/** Decodes a NextAuth.js issued JWT. */
export async function decodeLegacyJWT(token: string): Promise<JWT | null> {
if (!token) return null;
const encryptionSecret = await getDerivedEncryptionKey(
process.env.SECRET_PASSWORD,
"",
);
const { payload } = await jwtDecrypt(token, encryptionSecret, {
clockTolerance: 15,
});
return payload;
}
async function getDerivedEncryptionKey(
keyMaterial: string | Uint8Array,
salt: string,
) {
return await hkdf(
"sha256",
keyMaterial,
salt,
`NextAuth.js Generated Encryption Key${salt ? ` (${salt})` : ""}`,
32,
);
}

View file

@ -1,91 +0,0 @@
import { absoluteUrl } from "@rallly/utils/absolute-url";
import { cookies } from "next/headers";
import type { NextResponse } from "next/server";
import type { Session } from "next-auth";
import type { JWT } from "next-auth/jwt";
import { encode } from "next-auth/jwt";
import { decodeLegacyJWT } from "./helpers/jwt";
const isSecureCookie = absoluteUrl().startsWith("https://");
const prefix = isSecureCookie ? "__Secure-" : "";
const oldCookieName = prefix + "next-auth.session-token";
const newCookieName = prefix + "authjs.session-token";
export async function getLegacySession(): Promise<Session | null> {
const cookieStore = cookies();
const legacySessionCookie = cookieStore.get(oldCookieName);
if (legacySessionCookie && legacySessionCookie.value) {
const decodedCookie = await decodeLegacyJWT(legacySessionCookie.value);
if (decodedCookie?.sub) {
const { sub: id, ...rest } = decodedCookie;
return {
user: { id, ...rest },
expires: decodedCookie.exp
? new Date(decodedCookie.exp * 1000).toISOString()
: new Date(Date.now() + 30 * 60 * 60 * 1000).toISOString(),
};
}
}
return null;
}
async function getLegacyJWT() {
const cookieStore = cookies();
const legacySessionCookie = cookieStore.get(oldCookieName);
if (legacySessionCookie) {
const decodedCookie = await decodeLegacyJWT(legacySessionCookie.value);
if (decodedCookie) {
return decodedCookie;
}
}
return null;
}
export function deleteLegacyCookie(res: NextResponse) {
const cookieStore = cookies();
const oldCookie = cookieStore.get(oldCookieName);
if (oldCookie) {
// Delete the old cookie
res.cookies.set(oldCookieName, oldCookie.value, {
httpOnly: true,
secure: isSecureCookie,
expires: new Date(0),
sameSite: "lax",
path: "/",
});
}
}
async function setNewSessionCookie(res: NextResponse, jwt: JWT) {
const newJWT = await encode({
token: jwt,
secret: process.env.SECRET_PASSWORD,
salt: newCookieName,
});
// Set new session cookie
res.cookies.set(newCookieName, newJWT, {
httpOnly: true,
secure: isSecureCookie,
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7),
sameSite: "lax",
path: "/",
});
}
/**
* Replace the old legacy cookie with the new one
*/
export async function migrateLegacyJWT(res: NextResponse) {
const legacyJWT = await getLegacyJWT();
if (legacyJWT) {
await setNewSessionCookie(res, legacyJWT);
deleteLegacyCookie(res);
}
}

View file

@ -9,7 +9,6 @@ import z from "zod";
import { CustomPrismaAdapter } from "./auth/adapters/prisma"; import { CustomPrismaAdapter } from "./auth/adapters/prisma";
import { isEmailBanned, isEmailBlocked } from "./auth/helpers/is-email-blocked"; import { isEmailBanned, isEmailBlocked } from "./auth/helpers/is-email-blocked";
import { mergeGuestsIntoUser } from "./auth/helpers/merge-user"; import { mergeGuestsIntoUser } from "./auth/helpers/merge-user";
import { getLegacySession } from "./auth/legacy/next-auth-cookie-migration";
import { EmailProvider } from "./auth/providers/email"; import { EmailProvider } from "./auth/providers/email";
import { GoogleProvider } from "./auth/providers/google"; import { GoogleProvider } from "./auth/providers/google";
import { GuestProvider } from "./auth/providers/guest"; import { GuestProvider } from "./auth/providers/guest";
@ -203,12 +202,6 @@ const auth = cache(async () => {
} catch (e) { } catch (e) {
console.error("FAILED TO GET SESSION", e); console.error("FAILED TO GET SESSION", e);
} }
try {
return await getLegacySession();
} catch (e) {
console.error("FAILED TO GET LEGACY SESSION", e);
}
}); });
const requireUser = async () => { const requireUser = async () => {

View file

@ -1,57 +0,0 @@
import { expect, test } from "@playwright/test";
import { prisma } from "@rallly/database";
import { encode } from "./helpers/next-auth-v4";
const legacyGuestId = "user-1234";
test.describe.serial(() => {
test.beforeAll(async () => {
await prisma.poll.create({
data: {
id: "legacy-guest-poll",
title: "Test Poll",
adminUrlId: "admin-url-id",
participantUrlId: "participant-url-id",
guestId: legacyGuestId,
},
});
});
test.afterAll(async () => {
await prisma.poll.delete({
where: {
id: "legacy-guest-poll",
},
});
});
test("should see poll on login page", async ({ page }) => {
const context = page.context();
const legacyToken = await encode({
token: {
sub: legacyGuestId,
},
secret: process.env.SECRET_PASSWORD,
});
// set cookie to simulate legacy guest
await context.addCookies([
{
name: "next-auth.session-token",
value: legacyToken,
httpOnly: true,
expires: Date.now() / 1000 + 60 * 60 * 24 * 7,
secure: false,
sameSite: "Lax",
domain: "localhost",
path: "/",
},
]);
// For some reason it doesn't work unless we need to redirect
await page.goto("/login");
// Check if the poll title exists in the page content
await expect(page.getByText("Test Poll")).toBeVisible();
});
});