next-auth: useSession({ required: true }), which always returns a non-null Session object
Summary of proposed feature
A living session could be a requirement for specific pages. If it doesn’t exist, then the user should be redirected to the sign in page with an error like “Session expired, please try signing in again”.
Purpose of proposed feature
Sometimes, a user might log out by accident, or by deleting cookies on purpose. If that happens (e.g. on a separate tab), then useSession({ required: true }) should detect the absence of a session cookie and always return a non-nullable Session object type.
Detail about proposed feature
If the required option is specified, then an effect should be registered to redirect the user to the sign in page as soon as no session is available.
Potential problems
The session object could not only become nonexistent, but might even change over time. That edge case should be handled separately.
Describe any alternatives you’ve considered
Creating a hook in userland, e.g.:
function useSessionRequired() {
const [session, loading] = useSession();
const router = useRouter();
React.useEffect(() => {
if (!session && !loading) {
router.push(`${process.env.NEXTAUTH_URL}/auth/sign-in?error=SessionExpired`);
}
}, [loading, router, session]);
return [session, loading];
}
Additional context
As noticed in #1081, NextAuth.js already listens to page visibility changes. Session emptiness checks should be done each time the page becomes visible after hiding it.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 6
- Comments: 51 (11 by maintainers)
@kripod So I think I have found a very neat solution/pattern for this, let me know what you think!
It can be easily be extended/modified to support something like an options object for role based authentication on pages. An example:
Because of how
_appis done, it won’t unnecessarily contant the/api/auth/sessionendpoint for pages that do not require auth.@kripod Ok you’ve got me, needed to try this out before going to bed ^^
Here’s my TypeScript variant, with added types here and there:
In auth.utils.ts:
In _app.tsx:
In whatever page:
This seems to be working and feels relatively inoffensive/safe. The _app.tsx file uses
Partialjust to err on the safe side. No hard contract there, but at least it provides some structure.As I am too lazy to add
.authon every protected page, I did it bypathname.Authcomponent is the same.requireAuthis pretty much the thing I added.This way, under
/restricted, any pages will require a sign-in.Hi Guys,
Thank you @balazsorban44 for this pretty solution.
In case someone would like to have a server side redirect solution (eg. 307 HTTP Response) and session obtained on server side - here’s mine solution (may require further improvements):
Destination is custom user given, but can be modified to automatically go to sign in & redirect back url.
Usage with only auth protection:
Usage with additional user given serverSideProps:
Best Regards
@abdiweyrah You can replace this
for this
This is exactly what I’ve been looking for! Brilliant stuff. Good job @balazsorban44 ! Really helpful for those of us (me) new to NextJS let alone NextAuth 😃
Just beautiful approach ❤️ nice work!
@abdiweyrah I think you can extend your
AppPropslike thisexport type ProtectedAppProps = AppProps & { Component: NextComponentWithAuth }I know that has been closed for a while but I think there is always something to contribute. I did some improvements, especially in the TypeScript typings.
Basically, I’ve created the below type:
That allows me to use it on any Next.JS page like this:
And on
_applike this:I know this is closed, sorry for the necropost, but since the docs reference this issue as an explanation I was reading through anyway. I saw the excellent
react-queryimplementation and wanted to share a similar one I threw together using the awesome (somewhat similar)swrlibrary. I’d be open to publishing this at some point if there’s any interest. Also, please let me know if you spot a bug, missed edge case, or anything 😄Note: the
isEmptystuff is because the default fetcher that swr uses returns truthy{}and I prefer the!sessionergonomic.A nice summary of my approach from https://github.com/nextauthjs/next-auth/issues/1210#issuecomment-782630909 in an article form can be found here: https://simplernerd.com/next-auth-global-session
@kripod I still think your idea is valid, and I am working on a
useSession({ required: true })API change over at #2236.From now on, let us keep the discussion related to the OPs problem, as my suggestion is a workaround for this issue, rather than an actual solution. 😅
it works for me …thank you!!
I’m a bit inexperienced with TS. Where exactly is
NextComponentWithAuthbeing used? It’s in the_app.tsxfile but I don’t know what to do with it. UsingComponent.authgives an error that'auth'doesn’t exist.In my case i’'m using
useEffect=>useRouter&getInitialProps=>res.writeHeadto redirect user to login page. So finally now, i can prevent user from opening Authenticated page both server side & client side. Thank’s @balazsorban44 👍Folks using
react-query, check out https://github.com/nextauthjs/react-queryMy solution:
const { status, data: session } = useSession({ required: true })aand remember renaming
Component.authtoComponent.authenticationEnabledif you don’t pass the session from somewhere, then we have to fetch it asynchronously, and thus as you say there will be a brief moment with undefined session. not sure what else to say. we cannot make the session appear without actually fetching it first from somewhere. using getServerSideProps shifts this from blocking part of the UI to blocking as a whole. that’s the compromise.
it’s basically up to you to decide. if you don’t pass it there will be a flash of content when you don’t have an active session. you can mitigate it by for example adding a nice loading skeleton, like the Vercel dashboard. If you think it’s important that your user doesn’t see a screen like that and you are fine with increased TTFB, go with getServerSideProps
@zeing doesn’t have to be in the _app component’s body, you could extract it to its own component. besides, useRouter should work in _app anyway.
@wachidmudi glad it works for you! 🙂
it’s just a convention I had in our app. You’ll potentially end up with many Providers, so I just renamed it to better align with its purpose.