Next-Auth Signout within jwt callback
Asked Answered
C

2

10

If a user is signed into the app between app upgrades, then I want to sign out the user the next time they visit.

I am pulling the version in from the package.json and storing it in the jwt/session to have for comparison of which version the user authenticated from and what version is now running.

// pages/api/auth/[nextauth].ts
const version = require('../../../package.json').version
import NextAuth from 'next-auth'
import { signOut } from 'next-auth/react'

export default NextAuth({
  providers: [ /*...*/ ],
  session: { strategy: 'jwt' },
  callbacks: {
    async jwt(props) {
      const { token, user, account } = props
      const isSignIn = user?.username ? true :false
      if(isSignIn) {
        token.version = version
        // ...
      }
      if (token?.version !== version) {
        await signOut()
        return {}
      }
      return token
    },
    // ...
  }
})

This works, but it is throwing an error

https://next-auth.js.org/errors#jwt_session_error window is not defined {
  message: 'window is not defined',
  stack: ...
  name: 'ReferenceError
}

I know the error is throwing because I am calling the signOut function on the server side when it is meant to be a browser side function hence the error for window is not defined.

How else can I call the signOut function without it trying to use the browser side call including the reference to window?

Cayenne answered 4/3, 2022 at 18:38 Comment(1)
Here's an example from the docs that does exactly what you want: next-auth.js.org/tutorials/refresh-token-rotationWhelm
B
18

You can't use signOut on sever side from what I gathered, but here is a trick I used in my case. Instead trying signOut from the server side return an error code.

const version = require('../../../package.json').version
import NextAuth from 'next-auth'
import { signOut } from 'next-auth/react'

export default NextAuth({
  providers: [ /*...*/ ],
  session: { strategy: 'jwt' },
  callbacks: {
    async jwt(props) {
      const { token, user, account } = props
      const isSignIn = user?.username ? true :false
      if(isSignIn) {
        token.version = version
        // ...
      }
      if (token?.version !== version) {
        return {
        // You can ignore this if you don't need the previous data
         ...token,
        // Return an error code 
         error: "invalid-version",
        }
      }
      return token
    },
    // ...
  }
})

Create a wrapper around the secured routes. you can do this in a common layout too.

export const Auth = ({children}) => {
  const { data: sessionData } = useSession();
  const router = useRouter();

  useEffect(() => {
    // check if the error has occurred
    if (sessionData?.user.error === "invalid-version") {
        // Sign out here
        signOut();
    }
  }, [sessionData?.user.error, router]);

  return (
    <>{children}</>
  )
}
Blah answered 24/3, 2022 at 19:50 Comment(1)
Is this secure? I would have thought the client can prevent calling signOut and keep accessing secured resources.Tapestry
T
2

As previously mentioned, signOut() is client-side only. Using it on the server side on NextJS will give you that reference error.

Kwaku Hubert has a good solution of returning a responce.error that you can use in the client side if statement to then trigger signOut().

Next-Auth also has API endpoints you can call to do stuff. SignOut() just sends a POST to"/api/auth/signout" anyway. So you could just send a post to that on the server side rather than using signOut().

https://next-auth.js.org/getting-started/rest-api#post-apiauthsignout

Tellurate answered 29/4, 2023 at 4:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.