mirror of
https://github.com/lukevella/rallly.git
synced 2025-06-06 04:31:50 +02:00
🔥 Remove support for legacy sessions (#1654)
This commit is contained in:
parent
0bdceb7bbe
commit
13e078cb88
5 changed files with 0 additions and 219 deletions
|
@ -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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 () => {
|
||||||
|
|
|
@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Add table
Add a link
Reference in a new issue