Merged PR 2315: Auth stabilistatiion and hardening

Related work items: #23020
This commit is contained in:
Robert Bond
2026-05-14 08:55:49 +00:00
parent d295a507ac
commit d0b2fd077a
41 changed files with 1462 additions and 418 deletions
+73 -55
View File
@@ -14,35 +14,41 @@ import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";
import NextAuth from "next-auth";
import EmailProvider from "next-auth/providers/email";
import { consoleLogger } from "../../../actions/core/logger";
import { consoleLogger, redactSensitive } from "../../../actions/core/logger";
import { getPortalLogin } from "../../../actions/services/accountService";
const prisma = new PrismaClient();
const WELSH_LANGUAGE_CODE = 846040000;
const appendParamsAndPathToNewUrl = (fromUrl, toUrl) => {
const fromUrlObj = new URL(fromUrl);
const params = fromUrlObj.searchParams;
const toUrlObj = new URL(toUrl);
toUrlObj.pathname = fromUrlObj.pathname;
params.forEach((value, key) => {
if (!toUrlObj.searchParams.has(key)) {
toUrlObj.searchParams.append(key, value);
}
});
return toUrlObj.toString();
const authDiagnostic = (event, metadata = {}) => {
try {
const safeMeta = JSON.parse(JSON.stringify(metadata));
console.info(`[auth][${event}]`, redactSensitive(safeMeta));
} catch (_error) {
console.info(`[auth][${event}]`);
}
};
// const resolveRequestLocale = (req) => {
// const locale =
// req?.body?.locale || req?.query?.locale || req?.cookies?.pedw_locale;
const appendParamsAndPathToNewUrl = (fromUrl, toUrl) => {
try {
const fromUrlObj = new URL(fromUrl);
const params = fromUrlObj.searchParams;
// return locale === "cy" ? "cy" : "en";
// };
const toUrlObj = new URL(toUrl);
toUrlObj.pathname = fromUrlObj.pathname;
params.forEach((value, key) => {
if (!toUrlObj.searchParams.has(key)) {
toUrlObj.searchParams.append(key, value);
}
});
return toUrlObj.toString();
} catch (_error) {
return toUrl;
}
};
const resolveCrmLocale = async (email) => {
if (!email) return null;
@@ -69,9 +75,20 @@ const resolveCrmLocale = async (email) => {
const resolveEffectiveLocale = async (req, email) => {
const crmLocale = await resolveCrmLocale(email);
if (crmLocale) return crmLocale;
if (crmLocale) {
authDiagnostic("effective-locale.crm", {
locale: crmLocale,
hasEmail: !!email
});
return crmLocale;
}
return resolveRequestLocale(req);
const requestLocale = resolveRequestLocale(req);
authDiagnostic("effective-locale.request", {
locale: requestLocale,
hasEmail: !!email
});
return requestLocale;
};
const buildLocalizedVerificationUrl = ({ url, email, effectiveLocale }) => {
@@ -117,19 +134,35 @@ const getLocaleFromCallbackUrl = (callbackUrl) => {
const resolveRequestLocale = (req) => {
const directLocale =
req?.body?.locale || req?.query?.locale || req?.cookies?.pedw_locale;
req?.query?.locale || req?.body?.locale || req?.cookies?.pedw_locale;
if (directLocale === "cy") return "cy";
if (directLocale === "en") return "en";
if (directLocale === "cy") {
authDiagnostic("request-locale.direct", { locale: "cy" });
return "cy";
}
if (directLocale === "en") {
authDiagnostic("request-locale.direct", { locale: "en" });
return "en";
}
const callbackLocale =
getLocaleFromCallbackUrl(req?.body?.callbackUrl) ||
getLocaleFromCallbackUrl(req?.query?.callbackUrl) ||
getLocaleFromCallbackUrl(req?.cookies?.["next-auth.callback-url"]) ||
getLocaleFromCallbackUrl(
req?.cookies?.["__Secure-next-auth.callback-url"]
);
return callbackLocale === "cy" ? "cy" : "en";
const resolved = callbackLocale === "cy" ? "cy" : "en";
authDiagnostic("request-locale.callback-fallback", {
locale: resolved,
hasBodyCallback: !!req?.body?.callbackUrl,
hasQueryCallback: !!req?.query?.callbackUrl,
hasLegacyCookieCallback: !!req?.cookies?.["next-auth.callback-url"],
hasSecureCookieCallback:
!!req?.cookies?.["__Secure-next-auth.callback-url"]
});
return resolved;
};
const authOptions = (req, res) => {
@@ -147,24 +180,12 @@ const authOptions = (req, res) => {
email
);
console.log(
"============================================================\n",
"verification url API: " + url + "\n",
"============================================================\n"
);
const formURL = buildLocalizedVerificationUrl({
url,
email,
effectiveLocale
});
console.log(
"============================================================\n",
"new verification url----: " + formURL + "\n",
"============================================================\n"
);
return res
.writeHead(200, { "Content-Type": "application/json" })
.json({ url: formURL });
@@ -181,24 +202,12 @@ const authOptions = (req, res) => {
email
);
console.log(
"============================================================\n",
"verification url----: " + url + "\n",
"============================================================\n"
);
const formURL = buildLocalizedVerificationUrl({
url,
email,
effectiveLocale
});
console.log(
"============================================================\n",
"new verification url----: " + formURL + "\n",
"============================================================\n"
);
const personalisation = {
emailAddress: email,
signInLink: formURL,
@@ -261,15 +270,21 @@ const authOptions = (req, res) => {
},
callbacks: {
session: async (session, user) => {
console.log("callback in auth", session, user);
return Promise.resolve(session);
},
redirect({ url, baseUrl }) {
console.log("baseurl:", url, baseUrl);
if (url.startsWith("/")) return `${baseUrl}${url}`;
if (new URL(url).origin === baseUrl) return url;
try {
if (new URL(url).origin === baseUrl) return url;
} catch (_error) {
authDiagnostic("redirect.invalid-url", {
baseUrl,
hasUrl: !!url
});
return baseUrl;
}
const newUrl =
requestLocale === "cy"
@@ -277,7 +292,10 @@ const authOptions = (req, res) => {
: process.env.NEXTAUTH_URL;
const updatedUrl = appendParamsAndPathToNewUrl(url, newUrl);
console.log(updatedUrl);
authDiagnostic("redirect.external-rewrite", {
locale: requestLocale,
targetOrigin: newUrl
});
return updatedUrl;
}
}