Hello I'm currently trying to redirect a user back to the page they originally clicked on after being authenticated but it's very tricky because of SSR and not having access to window.history object on the get getServerSideProps. Before the page even loads since the auth wrapper function kicks the user to auth page and never reaches the original page the user clicks on.
import { getEnvVar } from '@helpers/utils'
import { AUTH_TOKEN_KEY } from '@helpers/constants'
export const readDecodedToken = (token: string | null) => {
if (!token) {
return null
}
const base64Payload = token.split('.')[1]
const payload = Buffer.from(base64Payload, 'base64')
return JSON.parse(payload.toString())
}
export function checkAuthRedirect(req, env) {
const { host } = req.headers
console.error('host:', host) // eslint-disable-line no-console
if (process.env.NEXT_PUBLIC_DANGEROUSLY_DISABLE_AUTH === 'true') {
return false
}
// For SDLC production environment, only require auth for non-prod content environments
const requireAuth =
process.env.NEXT_PUBLIC_STAGE === 'production'
? ['draft1', 'qa1', 'staging1', 'green.production'].some((contentEnv) => host.includes(contentEnv))
: true
console.info(requireAuth)
if (!requireAuth) {
return false
}
let decodedToken
try {
decodedToken = readDecodedToken(req.cookies[AUTH_TOKEN_KEY])
} catch (e) {
// If cookie exists (but unable to decode), log error
console.error(e)
}
// Token was properly decoded
if (decodedToken) {
return false
}
// Append env var to retrieve redirect URL
const authRedirectUrlBase = 'https://idp.3rdpartpage.com/as/authorization.oauth2'
const stage = process.env.NEXT_PUBLIC_STAGE as string
const clientId = `website-webapp-${stage}-content-${env}`
const redirectUri = `${process.env.NEXT_PUBLIC_FAPI_URL_BASE}/auth/web-${env}`
// Cannot use URLSearchParams here, as escaped strings aren't properly parsed by MyID
const authRedirectUrl = `${authRedirectUrlBase}?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=openid profile`
return authRedirectUrl
}
export function withRequireAuth(getServerSideProps) {
const getServerSidePropsWithRequireAuth = async (params) => {
const {
req: {
headers: { host },
},
} = params
const env = getEnvVar(host)
const authRedirect = checkAuthRedirect(params.req, env)
const shouldCache = process.env.NEXT_PUBLIC_STAGE === 'production' && env === 'prod'
if (shouldCache) {
params.res.setHeader('Cache-Control', 'max-age=300')
} else {
params.res.setHeader('Cache-Control', 'no-store')
}
if (authRedirect) {
return {
redirect: {
destination: authRedirect,
permanent: false,
},
}
}
return getServerSideProps(params)
}
return getServerSidePropsWithRequireAuth
}
This is the /auth page that Nextjs automatically routes to
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import Cookies from 'js-cookie'
import { AUTH_TOKEN_KEY } from '@helpers/constants'
import { styled } from '@mui/material/styles'
import CircularProgress from '@mui/material/CircularProgress'
import Alert from '@mui/material/Alert'
import AlertTitle from '@mui/material/AlertTitle'
const AuthResultContainer = styled('div')`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
`
export default function Auth() {
const router = useRouter()
const { fapi_token: token, expires } = router.query
const hasCredentials = token && expires
useEffect(() => {
if (hasCredentials) {
Cookies.set(AUTH_TOKEN_KEY, token, {
expires: new Date(Number(expires)),
})
// router.push('/') this need to be removed and replaced with the actual route that user came from
}
}, [token, expires, hasCredentials, router])
const resultComponent = hasCredentials ? (
<CircularProgress />
) : (
<Alert severity="error">
<AlertTitle>Authentication Error</AlertTitle>
Unable to authenticate due to <strong>missing MyID auth credentials</strong>
</Alert>
)
return <AuthResultContainer>{resultComponent}</AuthResultContainer>
}
On any page the user visit this is what the getServerSideProps looks like
export const getServerSideProps = withRequireAuth(async ({ req }) => {
const { host } = req.headers
const baseUrl = getHostUrl(host)
const env = getEnvVar(host)
const queryVariables = { env }
const { data, error } = await queryFapi(HomepageQuery, queryVariables, req)
const errorMessage = getApiErrorMessage(data, error)
const homepageData = data?.data ?? null
return {
props: { homepageData, baseUrl, errorMessage, host },
}
})
I have tried a few things to get it to work like replacing router.push('/')
to window.history.go(-2)
to go back two pages but when a user visits a route http://localhost:3000/watch
it would kick itself out of the page.
I also tried story the original page in the history but since the withRequireAuth runs before even page load it doesn't even reach the original it just gets kicked to the auth page.
Any help and suggestion is greatly appreciated
CodePudding user response:
using 'cookies-next' help me solve the issue
export const getServerSideProps = ({ req, res }) => {
const url = getCookies({ req, res }).redirectURL
return { props: { url } }
}