Merged PR 2318: auth stabilisation: extract shared myportal auth guard helper

auth stabilisation: extract shared myportal auth guard helper

ntroduces a small shared SSR helper (resolveMyPortalAuthContext) to standardize common myportal auth/session guards (session presence, session user identity, and pinsUser cookie) with preserved reason-coded diagnostics and signin redirect behavior. Migrates exactly two loaders (pages/myportal/searchresults.js, pages/myportal/addresssearchresults.js) to use the helper while keeping loader-specific UPSTREAM_FAILURE and CONTACT_LOOKUP_FAILED logic unchanged.

Related work items: #23020
This commit is contained in:
Robert Bond
2026-05-14 10:38:45 +00:00
parent 4fb62a6773
commit c2ae6964f9
5 changed files with 224 additions and 203 deletions
+58
View File
@@ -0,0 +1,58 @@
import { getSession } from "next-auth/react";
import { consoleLogger } from "../../actions/core/logger";
const signinRedirect = {
destination: "/auth/signin",
permanent: false
};
const buildSigninRedirectResult = () => ({ redirect: signinRedirect });
export async function resolveMyPortalAuthContext(ctx, options = {}) {
const {
loggerName = "MyPortalAuthGuard",
noSessionMessage = "myportal loader missing session; redirecting to signin",
noSessionUserMessage = "myportal loader missing session user identity; redirecting to signin",
noPinsUserMessage = "myportal loader missing pinsUser cookie; redirecting to signin"
} = options;
const session = await getSession(ctx);
if (!session) {
consoleLogger({
name: loggerName,
reasonCode: "NO_SESSION",
message: noSessionMessage
});
return { ok: false, redirect: buildSigninRedirectResult().redirect };
}
if (!session?.user?.id || !session?.user?.email) {
consoleLogger({
name: loggerName,
reasonCode: "NO_SESSION_USER",
message: noSessionUserMessage,
hasSessionUserId: !!session?.user?.id,
hasSessionUserEmail: !!session?.user?.email
});
return { ok: false, redirect: buildSigninRedirectResult().redirect };
}
const pinsUser = ctx?.req?.cookies?.pinsUser;
if (!pinsUser) {
consoleLogger({
name: loggerName,
reasonCode: "NO_PINSUSER_COOKIE",
message: noPinsUserMessage
});
return { ok: false, redirect: buildSigninRedirectResult().redirect };
}
return {
ok: true,
session,
sessionUserId: session.user.id,
sessionUserEmail: session.user.email,
pinsUser
};
}
+13 -48
View File
@@ -1,4 +1,3 @@
import { getSession } from "next-auth/react";
import useTranslation from "next-translate/useTranslation"; import useTranslation from "next-translate/useTranslation";
import Head from "next/head"; import Head from "next/head";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@@ -39,6 +38,7 @@ import {
import { wrapper } from "../../store/store"; import { wrapper } from "../../store/store";
import ServiceBanner from "../../components/myportal/servicebanner"; import ServiceBanner from "../../components/myportal/servicebanner";
import TimeOut from "../../components/timeout"; import TimeOut from "../../components/timeout";
import { resolveMyPortalAuthContext } from "../../lib/auth/resolveMyPortalAuthContext";
const Home = (props) => { const Home = (props) => {
const { footerLinks, pages } = props; const { footerLinks, pages } = props;
@@ -147,57 +147,23 @@ export const getServerSideProps = wrapper.getServerSideProps(
store.dispatch(setShowReps(showReps, showLoginCheck)); store.dispatch(setShowReps(showReps, showLoginCheck));
store.dispatch(setSearch(Object.entries(query))); store.dispatch(setSearch(Object.entries(query)));
const { cookies } = req; const authContext = await resolveMyPortalAuthContext(ctx, {
loggerName: "MyPortalAddressSearchResultsAuthGuard",
let loggedInUser = cookies.pinsUser; noSessionMessage:
let thisSession = await getSession(ctx); "myportal addresssearchresults loader missing session; redirecting to signin",
noSessionUserMessage:
if (!thisSession) {
consoleLogger({
name: "MyPortalAddressSearchResultsAuthGuard",
reasonCode: "NO_SESSION",
message:
"myportal addresssearchresults loader missing session; redirecting to signin"
});
return {
redirect: {
destination: "/auth/signin",
permanent: false
}
};
} else {
if (!thisSession?.user?.id || !thisSession?.user?.email) {
consoleLogger({
name: "MyPortalAddressSearchResultsAuthGuard",
reasonCode: "NO_SESSION_USER",
message:
"myportal addresssearchresults loader missing session user identity; redirecting to signin", "myportal addresssearchresults loader missing session user identity; redirecting to signin",
hasSessionUserId: !!thisSession?.user?.id, noPinsUserMessage:
hasSessionUserEmail: !!thisSession?.user?.email
});
return {
redirect: {
destination: "/auth/signin",
permanent: false
}
};
}
if (!loggedInUser) {
consoleLogger({
name: "MyPortalAddressSearchResultsAuthGuard",
reasonCode: "NO_PINSUSER_COOKIE",
message:
"myportal addresssearchresults loader missing pinsUser cookie; redirecting to signin" "myportal addresssearchresults loader missing pinsUser cookie; redirecting to signin"
}); });
return {
redirect: { if (!authContext.ok) {
destination: "/auth/signin", return { redirect: authContext.redirect };
permanent: false
}
};
} }
const thisSession = authContext.session;
const loggedInUser = authContext.pinsUser;
thisSession != false && thisSession != false &&
store.dispatch(setContainerID(thisSession.user.id)); store.dispatch(setContainerID(thisSession.user.id));
@@ -259,7 +225,6 @@ export const getServerSideProps = wrapper.getServerSideProps(
store.dispatch(setWatchedCasesDetails(watchedCasesDetails)); store.dispatch(setWatchedCasesDetails(watchedCasesDetails));
store.dispatch(setSearch(Object.entries(query))); store.dispatch(setSearch(Object.entries(query)));
store.dispatch(setLoggedInUserId(loggedInUser)); store.dispatch(setLoggedInUserId(loggedInUser));
}
return { return {
props: { props: {
+14 -50
View File
@@ -31,8 +31,8 @@ import {
setWatchedCasesDetails setWatchedCasesDetails
} from "../../store/watchedCases/action"; } from "../../store/watchedCases/action";
import { getSession } from "next-auth/react";
import TimeOut from "../../components/timeout"; import TimeOut from "../../components/timeout";
import { resolveMyPortalAuthContext } from "../../lib/auth/resolveMyPortalAuthContext";
import { import {
setAccountDetails, setAccountDetails,
setContainerID, setContainerID,
@@ -81,65 +81,29 @@ export const getServerSideProps = wrapper.getServerSideProps(
getIP(req); getIP(req);
console.log("query-", query); console.log("query-", query);
const { cookies } = req;
let thisSession = await getSession(ctx);
const showReps = process.env.SHOWREPRESENTATIONS || false; const showReps = process.env.SHOWREPRESENTATIONS || false;
const showLoginCheck = process.env.SHOWLOGIN || false; const showLoginCheck = process.env.SHOWLOGIN || false;
store.dispatch(setShowReps(showReps, showLoginCheck)); store.dispatch(setShowReps(showReps, showLoginCheck));
if (!thisSession) { const authContext = await resolveMyPortalAuthContext(ctx, {
consoleLogger({ loggerName: "MyPortalSearchResultsAuthGuard",
name: "MyPortalSearchResultsAuthGuard", noSessionMessage:
reasonCode: "NO_SESSION", "myportal searchresults loader missing session; redirecting to signin",
message: noSessionUserMessage:
"myportal searchresults loader missing session; redirecting to signin"
});
return {
redirect: {
destination: "/auth/signin",
permanent: false
}
};
} else {
if (!thisSession?.user?.id || !thisSession?.user?.email) {
consoleLogger({
name: "MyPortalSearchResultsAuthGuard",
reasonCode: "NO_SESSION_USER",
message:
"myportal searchresults loader missing session user identity; redirecting to signin", "myportal searchresults loader missing session user identity; redirecting to signin",
hasSessionUserId: !!thisSession?.user?.id, noPinsUserMessage:
hasSessionUserEmail: !!thisSession?.user?.email
});
return {
redirect: {
destination: "/auth/signin",
permanent: false
}
};
}
if (!cookies?.pinsUser) {
consoleLogger({
name: "MyPortalSearchResultsAuthGuard",
reasonCode: "NO_PINSUSER_COOKIE",
message:
"myportal searchresults loader missing pinsUser cookie; redirecting to signin" "myportal searchresults loader missing pinsUser cookie; redirecting to signin"
}); });
return {
redirect: { if (!authContext.ok) {
destination: "/auth/signin", return { redirect: authContext.redirect };
permanent: false
}
};
} }
const thisSession = authContext.session;
let loggedInUser; let loggedInUser;
try { try {
const portalLogin = await getPortalLogin( const portalLogin = await getPortalLogin(thisSession.user.email);
thisSession.user.email
);
loggedInUser = portalLogin?.value?.[0]?.contactid; loggedInUser = portalLogin?.value?.[0]?.contactid;
} catch (error) { } catch (error) {
consoleLogger({ consoleLogger({
@@ -190,7 +154,7 @@ export const getServerSideProps = wrapper.getServerSideProps(
thisSession != false && thisSession != false &&
store.dispatch(setContainerID(thisSession.user.id)); store.dispatch(setContainerID(thisSession.user.id));
}
return { return {
props: { props: {
showLoginCheck: showLoginCheck showLoginCheck: showLoginCheck
@@ -48,6 +48,11 @@ const loadMyPortalAddressSearchResultsPage = (overrides = {}) => {
setWatchedCases: () => ({}), setWatchedCases: () => ({}),
setWatchedCasesDetails: () => ({}), setWatchedCasesDetails: () => ({}),
setLoggedInUserId: () => ({}), setLoggedInUserId: () => ({}),
resolveMyPortalAuthContext: async (ctx) => ({
ok: true,
session: { user: { id: "u-1", email: "x@y.z" } },
pinsUser: ctx?.req?.cookies?.pinsUser
}),
wrapper: { wrapper: {
getServerSideProps: (factory) => async (ctx) => { getServerSideProps: (factory) => async (ctx) => {
const store = { dispatch: () => {} }; const store = { dispatch: () => {} };
@@ -65,7 +70,10 @@ const loadMyPortalAddressSearchResultsPage = (overrides = {}) => {
test("addresssearchresults redirects to signin when session is missing", async () => { test("addresssearchresults redirects to signin when session is missing", async () => {
const mod = loadMyPortalAddressSearchResultsPage({ const mod = loadMyPortalAddressSearchResultsPage({
getSession: async () => null resolveMyPortalAuthContext: async () => ({
ok: false,
redirect: { destination: "/auth/signin", permanent: false }
})
}); });
const result = await mod.getServerSideProps({ const result = await mod.getServerSideProps({
query: { q: "CAS" }, query: { q: "CAS" },
@@ -76,7 +84,10 @@ test("addresssearchresults redirects to signin when session is missing", async (
test("addresssearchresults redirects to signin when session user identity is missing", async () => { test("addresssearchresults redirects to signin when session user identity is missing", async () => {
const mod = loadMyPortalAddressSearchResultsPage({ const mod = loadMyPortalAddressSearchResultsPage({
getSession: async () => ({ user: { id: "u-1" } }) resolveMyPortalAuthContext: async () => ({
ok: false,
redirect: { destination: "/auth/signin", permanent: false }
})
}); });
const result = await mod.getServerSideProps({ const result = await mod.getServerSideProps({
query: { q: "CAS" }, query: { q: "CAS" },
@@ -86,7 +97,12 @@ test("addresssearchresults redirects to signin when session user identity is mis
}); });
test("addresssearchresults redirects to signin when pinsUser cookie is missing", async () => { test("addresssearchresults redirects to signin when pinsUser cookie is missing", async () => {
const mod = loadMyPortalAddressSearchResultsPage(); const mod = loadMyPortalAddressSearchResultsPage({
resolveMyPortalAuthContext: async () => ({
ok: false,
redirect: { destination: "/auth/signin", permanent: false }
})
});
const result = await mod.getServerSideProps({ const result = await mod.getServerSideProps({
query: { q: "CAS" }, query: { q: "CAS" },
req: { cookies: {} } req: { cookies: {} }
@@ -46,6 +46,11 @@ const loadMyPortalSearchResultsPage = (overrides = {}) => {
setWatchedCasesDetails: () => ({}), setWatchedCasesDetails: () => ({}),
setLoggedInUserId: () => ({}), setLoggedInUserId: () => ({}),
setContainerID: () => ({}), setContainerID: () => ({}),
resolveMyPortalAuthContext: async (ctx) => ({
ok: true,
session: { user: { id: "u-1", email: "test@example.com" } },
pinsUser: ctx?.req?.cookies?.pinsUser
}),
wrapper: { wrapper: {
getServerSideProps: (factory) => async (ctx) => { getServerSideProps: (factory) => async (ctx) => {
const store = { dispatch: () => {} }; const store = { dispatch: () => {} };
@@ -62,7 +67,12 @@ const loadMyPortalSearchResultsPage = (overrides = {}) => {
}; };
test("myportal searchresults redirects to signin when session is missing", async () => { test("myportal searchresults redirects to signin when session is missing", async () => {
const mod = loadMyPortalSearchResultsPage({ getSession: async () => null }); const mod = loadMyPortalSearchResultsPage({
resolveMyPortalAuthContext: async () => ({
ok: false,
redirect: { destination: "/auth/signin", permanent: false }
})
});
const result = await mod.getServerSideProps({ const result = await mod.getServerSideProps({
query: { q: "CAS" }, query: { q: "CAS" },
req: { cookies: { pinsUser: "contact-1" } } req: { cookies: { pinsUser: "contact-1" } }
@@ -72,7 +82,10 @@ test("myportal searchresults redirects to signin when session is missing", async
test("myportal searchresults redirects to signin when session user identity is missing", async () => { test("myportal searchresults redirects to signin when session user identity is missing", async () => {
const mod = loadMyPortalSearchResultsPage({ const mod = loadMyPortalSearchResultsPage({
getSession: async () => ({ user: { id: "u-1" } }) resolveMyPortalAuthContext: async () => ({
ok: false,
redirect: { destination: "/auth/signin", permanent: false }
})
}); });
const result = await mod.getServerSideProps({ const result = await mod.getServerSideProps({
query: { q: "CAS" }, query: { q: "CAS" },
@@ -82,7 +95,12 @@ test("myportal searchresults redirects to signin when session user identity is m
}); });
test("myportal searchresults redirects to signin when pinsUser cookie is missing", async () => { test("myportal searchresults redirects to signin when pinsUser cookie is missing", async () => {
const mod = loadMyPortalSearchResultsPage(); const mod = loadMyPortalSearchResultsPage({
resolveMyPortalAuthContext: async () => ({
ok: false,
redirect: { destination: "/auth/signin", permanent: false }
})
});
const result = await mod.getServerSideProps({ const result = await mod.getServerSideProps({
query: { q: "CAS" }, query: { q: "CAS" },
req: { cookies: {} } req: { cookies: {} }