From f61bfe18e8773a6a8b5cfdca248283d5058b6ad8 Mon Sep 17 00:00:00 2001 From: Jesse Anderson Date: Thu, 9 Dec 2021 17:47:39 -0500 Subject: [PATCH] feat: add generalized redirect AuthAction --- src/AuthAction.js | 1 + src/__tests__/AuthAction.test.js | 1 + src/__tests__/index.test.js | 1 + src/testHelpers/createMockConfig.js | 10 +++++++ src/withAuthUser.js | 41 +++++++++++++++++++++++++++++ src/withAuthUserTokenSSR.js | 34 ++++++++++++++++++++++++ 6 files changed, 88 insertions(+) diff --git a/src/AuthAction.js b/src/AuthAction.js index 432391dd..84a60965 100644 --- a/src/AuthAction.js +++ b/src/AuthAction.js @@ -6,6 +6,7 @@ const AuthAction = { RETURN_NULL: 'returnNull', REDIRECT_TO_LOGIN: 'redirectToLogin', REDIRECT_TO_APP: 'redirectToApp', + REDIRECT: 'redirect', } export default AuthAction diff --git a/src/__tests__/AuthAction.test.js b/src/__tests__/AuthAction.test.js index 50788141..0a750e35 100644 --- a/src/__tests__/AuthAction.test.js +++ b/src/__tests__/AuthAction.test.js @@ -8,6 +8,7 @@ describe('index.js: AuthAction', () => { RETURN_NULL: 'returnNull', REDIRECT_TO_LOGIN: 'redirectToLogin', REDIRECT_TO_APP: 'redirectToApp', + REDIRECT: 'redirect', }) }) }) diff --git a/src/__tests__/index.test.js b/src/__tests__/index.test.js index f76701c6..830e292e 100644 --- a/src/__tests__/index.test.js +++ b/src/__tests__/index.test.js @@ -205,6 +205,7 @@ describe('index.js: AuthAction', () => { RETURN_NULL: 'returnNull', REDIRECT_TO_LOGIN: 'redirectToLogin', REDIRECT_TO_APP: 'redirectToApp', + REDIRECT: 'redirect', }) }) }) diff --git a/src/testHelpers/createMockConfig.js b/src/testHelpers/createMockConfig.js index 651c871e..25b1c9ef 100644 --- a/src/testHelpers/createMockConfig.js +++ b/src/testHelpers/createMockConfig.js @@ -25,6 +25,16 @@ const createMockConfig = ({ clientSide } = {}) => { databaseURL: 'https://my-example-app.firebaseio.com', projectId: 'my-example-app-id', }, + onRedirect: { + whenAuthed: { + destination: '/', + permanent: false, + }, + whenUnauthed: { + destination: '/login', + permanent: false, + }, + }, cookies: { name: 'someExample', keys: useClientSideConfig ? [] : ['abc', 'def'], diff --git a/src/withAuthUser.js b/src/withAuthUser.js index 299ccbc8..0f884f6a 100644 --- a/src/withAuthUser.js +++ b/src/withAuthUser.js @@ -46,6 +46,7 @@ const withAuthUser = appPageURL = null, authPageURL = null, LoaderComponent = null, + onRedirect = null, } = {}) => (ChildComponent) => { const WithAuthUserHOC = (props) => { @@ -117,6 +118,12 @@ const withAuthUser = ? authRequestCompleted : true) + const shouldUseCustomRedirect = [ + whenAuthed, + whenUnauthedBeforeInit, + whenUnauthedAfterInit, + ].includes(AuthAction.REDIRECT) + const router = useRouter() const redirectToApp = useCallback(() => { logDebug('Redirecting to app.') @@ -160,6 +167,36 @@ const withAuthUser = } router.replace(destination) }, [router, AuthUser]) + const redirectToCustom = useCallback(() => { + logDebug('Redirecting to user-specified endpoint') + const redirectConfig = onRedirect || getConfig().onRedirect + if (!redirectConfig) { + throw new Error( + 'The "onRedirect" config setting must be set when using `REDIRECT`.' + ) + } + + const authStateConfig = isAuthed + ? onRedirect.whenAuthed + : onRedirect.whenUnauthed + + if (!authStateConfig || !authStateConfig.destination) { + throw new Error( + `The "destination" in the "onRedirect.whenAuthed" and "onRedirect.whenUnauthed" redirect configs must resolve to a non-empty string` + ) + } + + if ( + authStateConfig && + (!authStateConfig.destination || + typeof authStateConfig.destination !== 'string') + ) { + throw new Error( + 'The "destination" must be set to a non-empty string or resolve to a non-empty string' + ) + } + router.replace(authStateConfig.destination) + }, [router, isAuthed]) useEffect(() => { // Only redirect on the client side. To redirect server-side, @@ -171,12 +208,16 @@ const withAuthUser = redirectToApp() } else if (shouldRedirectToLogin) { redirectToLogin() + } else if (shouldUseCustomRedirect) { + redirectToCustom() } }, [ shouldRedirectToApp, shouldRedirectToLogin, + shouldUseCustomRedirect, redirectToApp, redirectToLogin, + redirectToCustom, ]) // Decide what to render. diff --git a/src/withAuthUserTokenSSR.js b/src/withAuthUserTokenSSR.js index a22275db..b113701b 100644 --- a/src/withAuthUserTokenSSR.js +++ b/src/withAuthUserTokenSSR.js @@ -38,6 +38,7 @@ const withAuthUserTokenSSR = whenUnauthed = AuthAction.RENDER, appPageURL = null, authPageURL = null, + onRedirect = null, } = {}, { useToken = true } = {} ) => @@ -144,6 +145,39 @@ const withAuthUserTokenSSR = } } + if ([whenAuthed, whenUnauthed].includes(AuthAction.REDIRECT)) { + const redirectConfig = onRedirect || getConfig().onRedirect + if (!redirectConfig) { + throw new Error( + `When "whenAuthed" or "whenUnauthed" are set to AuthAction.REDIRECT, "onRedirect" must be set.` + ) + } + + const authStateConfig = AuthUser.id + ? onRedirect.whenAuthed + : onRedirect.whenUnauthed + + if (!authStateConfig || !authStateConfig.destination) { + throw new Error( + `The "destination" in the "onRedirect.whenAuthed" and "onRedirect.whenUnauthed" redirect configs must resolve to a non-empty string` + ) + } + + if ( + authStateConfig && + (!authStateConfig.destination || + typeof authStateConfig.destination !== 'string') + ) { + throw new Error( + 'The "destination" must be set to a non-empty string or resolve to a non-empty string' + ) + } + + return { + redirect: authStateConfig, + } + } + // Prepare return data let returnData = { props: { AuthUserSerialized } }