Refactor authentication handling to pass authentication metadata into App.

eliminates warnings from oauth2 hook by conditionally using its useAuth hook.
This commit is contained in:
2025-04-05 19:37:02 -05:00
parent f654208769
commit e3d30633f1
4 changed files with 42 additions and 32 deletions

View File

@ -63,9 +63,14 @@ import {Md5} from "ts-md5/dist/md5";
const qController = Client.getInstance(); const qController = Client.getInstance();
export const SESSION_UUID_COOKIE_NAME = "sessionUUID"; export const SESSION_UUID_COOKIE_NAME = "sessionUUID";
export default function App() interface Props
{ {
const [cookies, setCookie, removeCookie] = useCookies([SESSION_UUID_COOKIE_NAME]); authenticationMetaData: QAuthenticationMetaData;
}
export default function App({authenticationMetaData}: Props)
{
const [, , removeCookie] = useCookies([SESSION_UUID_COOKIE_NAME]);
const [loadingToken, setLoadingToken] = useState(false); const [loadingToken, setLoadingToken] = useState(false);
const [isFullyAuthenticated, setIsFullyAuthenticated] = useState(false); const [isFullyAuthenticated, setIsFullyAuthenticated] = useState(false);
const [profileRoutes, setProfileRoutes] = useState({}); const [profileRoutes, setProfileRoutes] = useState({});
@ -74,11 +79,10 @@ export default function App()
const [needLicenseKey, setNeedLicenseKey] = useState(true); const [needLicenseKey, setNeedLicenseKey] = useState(true);
const [loggedInUser, setLoggedInUser] = useState({} as { name?: string, email?: string }); const [loggedInUser, setLoggedInUser] = useState({} as { name?: string, email?: string });
const [defaultRoute, setDefaultRoute] = useState("/no-apps"); const [defaultRoute, setDefaultRoute] = useState("/no-apps");
const [authenticationMetaData, setAuthenticationMetaData] = useState(null as QAuthenticationMetaData | null);
const [earlyReturnForAuth, setEarlyReturnForAuth] = useState(null as JSX.Element); const [earlyReturnForAuth, setEarlyReturnForAuth] = useState(null as JSX.Element);
const {setupSession: auth0SetupSession, logout: auth0Logout} = useAuth0AuthenticationModule({setIsFullyAuthenticated, setLoggedInUser, setEarlyReturnForAuth}); const {setupSession: auth0SetupSession, logout: auth0Logout} = useAuth0AuthenticationModule({setIsFullyAuthenticated, setLoggedInUser, setEarlyReturnForAuth});
const {setupSession: oauth2SetupSession, logout: oauth2Logout} = useOAuth2AuthenticationModule({setIsFullyAuthenticated, setLoggedInUser, setEarlyReturnForAuth}); const {setupSession: oauth2SetupSession, logout: oauth2Logout} = useOAuth2AuthenticationModule({setIsFullyAuthenticated, setLoggedInUser, setEarlyReturnForAuth, inOAuthContext: authenticationMetaData.type === "OAUTH2"});
const {setupSession: anonymousSetupSession, logout: anonymousLogout} = useAnonymousAuthenticationModule({setIsFullyAuthenticated, setLoggedInUser, setEarlyReturnForAuth}); const {setupSession: anonymousSetupSession, logout: anonymousLogout} = useAnonymousAuthenticationModule({setIsFullyAuthenticated, setLoggedInUser, setEarlyReturnForAuth});
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
@ -99,9 +103,6 @@ export default function App()
(async () => (async () =>
{ {
const authenticationMetaData: QAuthenticationMetaData = await qController.getAuthenticationMetaData();
setAuthenticationMetaData(authenticationMetaData);
if (authenticationMetaData.type === "AUTH_0") if (authenticationMetaData.type === "AUTH_0")
{ {
await auth0SetupSession(); await auth0SetupSession();
@ -134,7 +135,7 @@ export default function App()
} }
/*************************************************************************** /***************************************************************************
** call approprite logout function based on authentication meta data type ** call appropriate logout function based on authentication meta data type
***************************************************************************/ ***************************************************************************/
function doLogout() function doLogout()
{ {
@ -157,7 +158,7 @@ export default function App()
} }
const [controller, dispatch] = useMaterialUIController(); const [controller, dispatch] = useMaterialUIController();
const {miniSidenav, direction, layout, openConfigurator, sidenavColor} = controller; const {miniSidenav, direction, sidenavColor} = controller;
const [onMouseEnter, setOnMouseEnter] = useState(false); const [onMouseEnter, setOnMouseEnter] = useState(false);
const {pathname} = useLocation(); const {pathname} = useLocation();
const [queryParams] = useSearchParams(); const [queryParams] = useSearchParams();
@ -450,11 +451,10 @@ export default function App()
} }
} }
let profileRoutes = {};
const gravatarBase = "https://www.gravatar.com/avatar/"; const gravatarBase = "https://www.gravatar.com/avatar/";
const hash = Md5.hashStr(loggedInUser?.email || "user"); const hash = Md5.hashStr(loggedInUser?.email || "user");
const profilePicture = `${gravatarBase}${hash}`; const profilePicture = `${gravatarBase}${hash}`;
profileRoutes = { const profileRoutes = {
type: "collapse", type: "collapse",
name: loggedInUser?.name ?? "Anonymous", name: loggedInUser?.name ?? "Anonymous",
key: "username", key: "username",

View File

@ -52,7 +52,7 @@ authenticationMetaDataPromise.then((authenticationMetaData) =>
function Auth0RouterBody() function Auth0RouterBody()
{ {
const {renderAppWrapper} = useAuth0AuthenticationModule({}); const {renderAppWrapper} = useAuth0AuthenticationModule({});
return (renderAppWrapper(authenticationMetaData, null)); return (renderAppWrapper(authenticationMetaData));
} }
@ -61,10 +61,10 @@ authenticationMetaDataPromise.then((authenticationMetaData) =>
***************************************************************************/ ***************************************************************************/
function OAuth2RouterBody() function OAuth2RouterBody()
{ {
const {renderAppWrapper} = useOAuth2AuthenticationModule({}); const {renderAppWrapper} = useOAuth2AuthenticationModule({inOAuthContext: false});
return (renderAppWrapper(authenticationMetaData, ( return (renderAppWrapper(authenticationMetaData, (
<MaterialUIControllerProvider> <MaterialUIControllerProvider>
<App /> <App authenticationMetaData={authenticationMetaData} />
</MaterialUIControllerProvider> </MaterialUIControllerProvider>
))); )));
} }
@ -78,7 +78,7 @@ authenticationMetaDataPromise.then((authenticationMetaData) =>
const {renderAppWrapper} = useAnonymousAuthenticationModule({}); const {renderAppWrapper} = useAnonymousAuthenticationModule({});
return (renderAppWrapper(authenticationMetaData, ( return (renderAppWrapper(authenticationMetaData, (
<MaterialUIControllerProvider> <MaterialUIControllerProvider>
<App /> <App authenticationMetaData={authenticationMetaData} />
</MaterialUIControllerProvider> </MaterialUIControllerProvider>
))); )));
} }

View File

@ -41,11 +41,11 @@ interface Props
/*************************************************************************** /***************************************************************************
** hook for working with the Auth0 authentication module ** hook for working with the Auth0 authentication module
***************************************************************************/ ***************************************************************************/
export default function useAuth0AuthenticationModule({setIsFullyAuthenticated, setLoggedInUser, setEarlyReturnForAuth}: Props) export default function useAuth0AuthenticationModule({setIsFullyAuthenticated, setLoggedInUser}: Props)
{ {
const {user: auth0User, getAccessTokenSilently: auth0GetAccessTokenSilently, logout: useAuth0Logout} = useAuth0(); const {user: auth0User, getAccessTokenSilently: auth0GetAccessTokenSilently, logout: useAuth0Logout} = useAuth0();
const [cookies, setCookie, removeCookie] = useCookies([SESSION_UUID_COOKIE_NAME]); const [cookies, removeCookie] = useCookies([SESSION_UUID_COOKIE_NAME]);
/*************************************************************************** /***************************************************************************
@ -119,12 +119,7 @@ export default function useAuth0AuthenticationModule({setIsFullyAuthenticated, s
if (shouldStoreNewToken(accessToken, lsAccessToken)) if (shouldStoreNewToken(accessToken, lsAccessToken))
{ {
console.log("Sending accessToken to backend, requesting a sessionUUID..."); console.log("Sending accessToken to backend, requesting a sessionUUID...");
const {uuid: newSessionUuid, values} = await qController.manageSession(accessToken, null); const {uuid: values} = await qController.manageSession(accessToken, null);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// the request to the backend should send a header to set the cookie, so we don't need to do it ourselves. //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// setCookie(SESSION_UUID_COOKIE_NAME, newSessionUuid, {path: "/"});
localStorage.setItem("accessToken", accessToken); localStorage.setItem("accessToken", accessToken);
localStorage.setItem("sessionValues", JSON.stringify(values)); localStorage.setItem("sessionValues", JSON.stringify(values));
@ -199,7 +194,7 @@ export default function useAuth0AuthenticationModule({setIsFullyAuthenticated, s
/*************************************************************************** /***************************************************************************
** **
***************************************************************************/ ***************************************************************************/
const renderAppWrapper = (authenticationMetaData: QAuthenticationMetaData, children: JSX.Element): JSX.Element => const renderAppWrapper = (authenticationMetaData: QAuthenticationMetaData): JSX.Element =>
{ {
// @ts-ignore // @ts-ignore
let domain: string = authenticationMetaData.data.baseUrl; let domain: string = authenticationMetaData.data.baseUrl;
@ -225,6 +220,15 @@ export default function useAuth0AuthenticationModule({setIsFullyAuthenticated, s
domain = domain.replace(/\/$/, ""); domain = domain.replace(/\/$/, "");
} }
/***************************************************************************
** simple Functional Component to wrap the <App> and pass the authentication-
** MetaData prop in, so a simple Component can be passed into ProtectedRoute
***************************************************************************/
function WrappedApp()
{
return <App authenticationMetaData={authenticationMetaData} />
}
return ( return (
<Auth0ProviderWithRedirectCallback <Auth0ProviderWithRedirectCallback
domain={domain} domain={domain}
@ -232,7 +236,7 @@ export default function useAuth0AuthenticationModule({setIsFullyAuthenticated, s
audience={audience} audience={audience}
redirectUri={`${window.location.origin}/`}> redirectUri={`${window.location.origin}/`}>
<MaterialUIControllerProvider> <MaterialUIControllerProvider>
<ProtectedRoute component={App} /> <ProtectedRoute component={WrappedApp} />
</MaterialUIControllerProvider> </MaterialUIControllerProvider>
</Auth0ProviderWithRedirectCallback> </Auth0ProviderWithRedirectCallback>
); );

View File

@ -23,7 +23,7 @@ import {QAuthenticationMetaData} from "@kingsrook/qqq-frontend-core/lib/model/me
import {SESSION_UUID_COOKIE_NAME} from "App"; import {SESSION_UUID_COOKIE_NAME} from "App";
import Client from "qqq/utils/qqq/Client"; import Client from "qqq/utils/qqq/Client";
import {useCookies} from "react-cookie"; import {useCookies} from "react-cookie";
import {AuthProvider, useAuth} from "react-oidc-context"; import {AuthContextProps, AuthProvider, useAuth} from "react-oidc-context";
import {useNavigate, useSearchParams} from "react-router-dom"; import {useNavigate, useSearchParams} from "react-router-dom";
const qController = Client.getInstance(); const qController = Client.getInstance();
@ -33,16 +33,22 @@ interface Props
setIsFullyAuthenticated?: (is: boolean) => void; setIsFullyAuthenticated?: (is: boolean) => void;
setLoggedInUser?: (user: any) => void; setLoggedInUser?: (user: any) => void;
setEarlyReturnForAuth?: (element: JSX.Element | null) => void; setEarlyReturnForAuth?: (element: JSX.Element | null) => void;
inOAuthContext: boolean;
} }
/*************************************************************************** /***************************************************************************
** hook for working with the OAuth2 authentication module ** hook for working with the OAuth2 authentication module
***************************************************************************/ ***************************************************************************/
export default function useOAuth2AuthenticationModule({setIsFullyAuthenticated, setLoggedInUser, setEarlyReturnForAuth}: Props) export default function useOAuth2AuthenticationModule({setIsFullyAuthenticated, setLoggedInUser, setEarlyReturnForAuth, inOAuthContext}: Props)
{ {
const authOidc = useAuth(); ///////////////////////////////////////////////////////////////////////////////////////
// the useAuth hook should only be called if we're inside the <AuthProvider> element //
// so on the page that uses this hook to call renderAppWrapper, we aren't in that //
// element/context, thus, don't call that hook. //
///////////////////////////////////////////////////////////////////////////////////////
const authOidc: AuthContextProps | null = inOAuthContext ? useAuth() : null;
const [cookies, setCookie, removeCookie] = useCookies([SESSION_UUID_COOKIE_NAME]); const [cookies, removeCookie] = useCookies([SESSION_UUID_COOKIE_NAME]);
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const navigate = useNavigate(); const navigate = useNavigate();
@ -84,7 +90,7 @@ export default function useOAuth2AuthenticationModule({setIsFullyAuthenticated,
if (sessionUuid) if (sessionUuid)
{ {
console.log(`we have session UUID: ${sessionUuid} - validating it...`); console.log(`we have session UUID: ${sessionUuid} - validating it...`);
const {uuid: newSessionUuid, values} = await qController.manageSession(null, sessionUuid, null); const {values} = await qController.manageSession(null, sessionUuid, null);
setIsFullyAuthenticated(true); setIsFullyAuthenticated(true);
qController.setGotAuthentication(); qController.setGotAuthentication();
@ -98,7 +104,7 @@ export default function useOAuth2AuthenticationModule({setIsFullyAuthenticated,
console.log(authOidc); console.log(authOidc);
localStorage.setItem(preSigninRedirectPathnameKey, window.location.pathname); localStorage.setItem(preSigninRedirectPathnameKey, window.location.pathname);
setEarlyReturnForAuth(<div>Signing in...</div>); setEarlyReturnForAuth(<div>Signing in...</div>);
authOidc.signinRedirect(); authOidc?.signinRedirect();
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -151,7 +157,7 @@ export default function useOAuth2AuthenticationModule({setIsFullyAuthenticated,
{ {
qController.clearAuthenticationMetaDataLocalStorage(); qController.clearAuthenticationMetaDataLocalStorage();
removeCookie(SESSION_UUID_COOKIE_NAME, {path: "/"}); removeCookie(SESSION_UUID_COOKIE_NAME, {path: "/"});
authOidc.signoutRedirect(); authOidc?.signoutRedirect();
}; };