Merged PR 2315: Auth stabilistatiion and hardening
Related work items: #23020
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user