refactor: centralize token configuration and update cookie handling in auth middleware and controllers

This commit is contained in:
Marco Pedone 2025-09-04 12:35:44 +02:00
parent 1aca72ac69
commit d5894f6ec4
5 changed files with 69 additions and 68 deletions

View file

@ -53,8 +53,8 @@ export const FormLogin = () => {
await utils.auth.getSession.invalidate(); await utils.auth.getSession.invalidate();
setAlreadyRequested(true); setAlreadyRequested(true);
toast.success(t.auth.login.success); toast.success(t.auth.login.success);
//await router.push(redirect ? (redirect as string) : "/"); await router.push(redirect ? (redirect as string) : "/");
window.location.replace(redirect ? (redirect as string) : "/"); //window.location.replace(redirect ? (redirect as string) : "/");
}, },
}); });
const [altreadyRequested, setAlreadyRequested] = useState(false); const [altreadyRequested, setAlreadyRequested] = useState(false);

View file

@ -1,20 +1,19 @@
import { add } from "date-fns"; import { add } from "date-fns";
import { type NextRequest, NextResponse } from "next/server"; import { type NextRequest, NextResponse } from "next/server";
import { env } from "~/env.mjs"; import { TOKEN_CONFIG } from "~/server/auth/configs";
import { import { signToken, verifyToken } from "~/server/auth/jwt";
ACCESS_TOKEN_COOKIE_NAME,
REFRESH_TOKEN_COOKIE_NAME,
signToken,
verifyToken,
} from "~/server/auth/jwt";
export const authMiddleware = async (req: NextRequest) => { export const authMiddleware = async (req: NextRequest) => {
const path = req.nextUrl.pathname; const path = req.nextUrl.pathname;
const refreshToken = req.cookies.get(REFRESH_TOKEN_COOKIE_NAME)?.value; const refreshToken = req.cookies.get(
TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME,
)?.value;
const accessToken = req.cookies.get(ACCESS_TOKEN_COOKIE_NAME)?.value; const accessToken = req.cookies.get(
TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME,
)?.value;
const isProtectedRoute = const isProtectedRoute =
(path.startsWith("/area-riservata") || path.startsWith("/servizio")) && (path.startsWith("/area-riservata") || path.startsWith("/servizio")) &&
@ -27,8 +26,8 @@ export const authMiddleware = async (req: NextRequest) => {
const destination = new URL("/login", req.nextUrl); const destination = new URL("/login", req.nextUrl);
destination.searchParams.set("redirect", path); destination.searchParams.set("redirect", path);
const response = NextResponse.redirect(destination); const response = NextResponse.redirect(destination);
response.cookies.delete(ACCESS_TOKEN_COOKIE_NAME); response.cookies.delete(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME);
response.cookies.delete(REFRESH_TOKEN_COOKIE_NAME); response.cookies.delete(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME);
return response; return response;
} }
// if refresh token is present, handle refresh token logic // if refresh token is present, handle refresh token logic
@ -42,7 +41,7 @@ export const authMiddleware = async (req: NextRequest) => {
const destination = new URL("/login", req.nextUrl); const destination = new URL("/login", req.nextUrl);
destination.searchParams.set("redirect", path); destination.searchParams.set("redirect", path);
const response = NextResponse.redirect(destination); const response = NextResponse.redirect(destination);
response.cookies.delete(ACCESS_TOKEN_COOKIE_NAME); response.cookies.delete(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME);
return response; return response;
} }
@ -61,8 +60,8 @@ export const authMiddleware = async (req: NextRequest) => {
return await refreshAuthToken(req, refreshToken, true); return await refreshAuthToken(req, refreshToken, true);
} }
const response = NextResponse.next(); const response = NextResponse.next();
response.cookies.delete(ACCESS_TOKEN_COOKIE_NAME); response.cookies.delete(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME);
response.cookies.delete(REFRESH_TOKEN_COOKIE_NAME); response.cookies.delete(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME);
return response; return response;
} }
@ -88,7 +87,7 @@ export const authMiddleware = async (req: NextRequest) => {
const destination = new URL("/login", req.nextUrl); const destination = new URL("/login", req.nextUrl);
destination.searchParams.set("redirect", path); destination.searchParams.set("redirect", path);
const response = NextResponse.redirect(destination); const response = NextResponse.redirect(destination);
response.cookies.delete(ACCESS_TOKEN_COOKIE_NAME); response.cookies.delete(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME);
return response; return response;
} }
if (path.startsWith("/auth/new-password-reset")) { if (path.startsWith("/auth/new-password-reset")) {
@ -121,7 +120,7 @@ const refreshAuthToken = async (
const destination = new URL("/login", req.nextUrl); const destination = new URL("/login", req.nextUrl);
destination.searchParams.set("redirect", req.nextUrl.pathname); destination.searchParams.set("redirect", req.nextUrl.pathname);
const response = NextResponse.redirect(destination); const response = NextResponse.redirect(destination);
response.cookies.delete(REFRESH_TOKEN_COOKIE_NAME); response.cookies.delete(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME);
return response; return response;
} }
// If refresh token is valid, sign a new access token // If refresh token is valid, sign a new access token
@ -133,14 +132,14 @@ const refreshAuthToken = async (
const newAccessToken = await signToken( const newAccessToken = await signToken(
refreshTokenPayload, refreshTokenPayload,
"5m", TOKEN_CONFIG.ACCESS_TOKEN_EXPIRY,
"accessToken", "accessToken",
); );
response.cookies.set(ACCESS_TOKEN_COOKIE_NAME, newAccessToken, { response.cookies.set(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME, newAccessToken, {
expires: add(new Date(), { minutes: 1 }), expires: add(new Date(), {
httpOnly: true, minutes: TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_EXPIRY,
path: "/", }),
sameSite: "lax", ...TOKEN_CONFIG.COOKIE_OPTIONS,
//secure: env.NODE_ENV === "production", //secure: env.NODE_ENV === "production",
}); });
return response; return response;

View file

@ -0,0 +1,13 @@
export const TOKEN_CONFIG = {
ACCESS_TOKEN_COOKIE_NAME: "access_token",
REFRESH_TOKEN_COOKIE_NAME: "refresh_token",
ACCESS_TOKEN_EXPIRY: "5m",
REFRESH_TOKEN_EXPIRY: "7d",
ACCESS_TOKEN_COOKIE_EXPIRY: 5,
REFRESH_TOKEN_COOKIE_EXPIRY: 7,
COOKIE_OPTIONS: {
httpOnly: true,
path: "/",
sameSite: "lax",
},
} as const;

View file

@ -3,9 +3,6 @@ import { jwtVerify, SignJWT } from "jose";
import { env } from "~/env.mjs"; import { env } from "~/env.mjs";
import { type Session, sessionSchema } from "~/server/api/trpc"; import { type Session, sessionSchema } from "~/server/api/trpc";
export const ACCESS_TOKEN_COOKIE_NAME = "access_token";
export const REFRESH_TOKEN_COOKIE_NAME = "refresh_token";
const secret = new TextEncoder().encode(env.JWT_SECRET); const secret = new TextEncoder().encode(env.JWT_SECRET);
export async function signToken( export async function signToken(

View file

@ -1,13 +1,9 @@
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { deleteCookie, type OptionsType, setCookie } from "cookies-next"; import { deleteCookie, setCookie } from "cookies-next";
import { add } from "date-fns"; import { add } from "date-fns";
import type { UsersId } from "~/schemas/public/Users"; import type { UsersId } from "~/schemas/public/Users";
import type { Context } from "~/server/api/trpc"; import type { Context } from "~/server/api/trpc";
import { import { signToken } from "~/server/auth/jwt";
ACCESS_TOKEN_COOKIE_NAME,
REFRESH_TOKEN_COOKIE_NAME,
signToken,
} from "~/server/auth/jwt";
import { db } from "~/server/db"; import { db } from "~/server/db";
import { isBanned } from "~/server/services/banlist.service"; import { isBanned } from "~/server/services/banlist.service";
import { import {
@ -22,27 +18,7 @@ import {
findUser_byId_MINI, findUser_byId_MINI,
} from "~/server/services/user.service"; } from "~/server/services/user.service";
import { checkCtx } from "~/server/utils/ctxChecker"; import { checkCtx } from "~/server/utils/ctxChecker";
import { TOKEN_CONFIG } from "../auth/configs";
// [...] Cookie options
const cookieOptions: OptionsType = {
httpOnly: true,
path: "/",
sameSite: "lax",
//secure: env.NODE_ENV === "production",
};
const accessTokenExpiry = "1m";
export const refreshTokenExpiry = "5m";
const accessTokenCookieOptions: OptionsType = {
...cookieOptions,
expires: add(new Date(), { minutes: 1 }),
};
const refreshTokenCookieOptions: OptionsType = {
...cookieOptions,
expires: add(new Date(), { minutes: 5 }),
};
export const createUserAdmin = async ({ export const createUserAdmin = async ({
email, email,
@ -153,24 +129,30 @@ export const logIn = async ({
const accessToken = await signToken( const accessToken = await signToken(
payload, payload,
accessTokenExpiry, TOKEN_CONFIG.ACCESS_TOKEN_EXPIRY,
"accessToken", "accessToken",
); );
const refreshToken = await signToken( const refreshToken = await signToken(
payload, payload,
refreshTokenExpiry, TOKEN_CONFIG.REFRESH_TOKEN_EXPIRY,
"refreshToken", "refreshToken",
); );
console.log("Generated tokens for user:", user.email); console.log("Generated tokens for user:", user.email);
try { try {
await setCookie(ACCESS_TOKEN_COOKIE_NAME, accessToken, { await setCookie(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME, accessToken, {
...accessTokenCookieOptions, ...TOKEN_CONFIG.COOKIE_OPTIONS,
expires: add(new Date(), {
minutes: TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_EXPIRY,
}),
req, req,
res, res,
}); });
await setCookie(REFRESH_TOKEN_COOKIE_NAME, refreshToken, { await setCookie(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME, refreshToken, {
...refreshTokenCookieOptions, ...TOKEN_CONFIG.COOKIE_OPTIONS,
expires: add(new Date(), {
minutes: TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_EXPIRY,
}),
req, req,
res, res,
}); });
@ -209,21 +191,31 @@ export const swapAuth = async ({
}); });
} }
const accessToken = await signToken(user, accessTokenExpiry, "accessToken"); const accessToken = await signToken(
user,
TOKEN_CONFIG.ACCESS_TOKEN_EXPIRY,
"accessToken",
);
const refreshToken = await signToken( const refreshToken = await signToken(
user, user,
refreshTokenExpiry, TOKEN_CONFIG.REFRESH_TOKEN_EXPIRY,
"refreshToken", "refreshToken",
); );
// Send Access Token in Cookie // Send Access Token in Cookie
await setCookie(ACCESS_TOKEN_COOKIE_NAME, accessToken, { await setCookie(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME, accessToken, {
...accessTokenCookieOptions, ...TOKEN_CONFIG.COOKIE_OPTIONS,
expires: add(new Date(), {
minutes: TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_EXPIRY,
}),
req, req,
res, res,
}); });
await setCookie(REFRESH_TOKEN_COOKIE_NAME, refreshToken, { await setCookie(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME, refreshToken, {
...refreshTokenCookieOptions, ...TOKEN_CONFIG.COOKIE_OPTIONS,
expires: add(new Date(), {
minutes: TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_EXPIRY,
}),
req, req,
res, res,
}); });
@ -238,8 +230,8 @@ export const swapAuth = async ({
export const logOut = async ({ ctx: { req, res } }: { ctx: Context }) => { export const logOut = async ({ ctx: { req, res } }: { ctx: Context }) => {
checkCtx({ req, res }); checkCtx({ req, res });
try { try {
await deleteCookie(ACCESS_TOKEN_COOKIE_NAME, { req, res }); await deleteCookie(TOKEN_CONFIG.ACCESS_TOKEN_COOKIE_NAME, { req, res });
await deleteCookie(REFRESH_TOKEN_COOKIE_NAME, { req, res }); await deleteCookie(TOKEN_CONFIG.REFRESH_TOKEN_COOKIE_NAME, { req, res });
return { status: "success" }; return { status: "success" };
} catch (err: unknown) { } catch (err: unknown) {
console.error(err); console.error(err);