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:
@@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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: {} }
|
||||||
|
|||||||
Reference in New Issue
Block a user