Replies: 26 comments 20 replies
-
We've been receiving some feedback about having signOut on the Backend, but haven't decided yet about the API 🤔 Prolly we will release it in an experimental fashion in the near future. 🤞 |
Beta Was this translation helpful? Give feedback.
-
Hi @ThangHuuVu. Do you have any new informations when signing out from backend side will be possible? :) |
Beta Was this translation helpful? Give feedback.
-
Any update on this? I'm currently using the I've spent countless days trying to get this working on an enterprise app and it seems like NextAuth just really doesn't want me using the |
Beta Was this translation helpful? Give feedback.
-
I also need server side logout.. I can't really use JWT / Session callbacks are they are called a lot. in my case I really juste want to do the verification once (on every refresh basically) Any plan for this to actually be a thing? Or potential workarounds? |
Beta Was this translation helpful? Give feedback.
-
hello, any info for this?, I have same question |
Beta Was this translation helpful? Give feedback.
-
Hello @wahyunurarizky, I had the same problem, but in my case, I wanted to log out the user when the backend API JWT token expired as I'm using a single fetch instance I created for the entire project. Also as for the project I opted to use server action for API calls and data fetching but the signout function seems to be a hook meaning it can't be called on the server side, so, to handle this I created a standalone client page log out page and use |
Beta Was this translation helpful? Give feedback.
-
Please is there any new update on this ? |
Beta Was this translation helpful? Give feedback.
-
Any update on this? Disappointing for such a simple thing to be so inelegant |
Beta Was this translation helpful? Give feedback.
-
Man, I am in soo need of this rn. |
Beta Was this translation helpful? Give feedback.
-
Would be awesome if we could get an update on this 🙏 |
Beta Was this translation helpful? Give feedback.
-
Probably a good idea to verify the csrf token here e.g.
Btw, it seems like a bad idea to clear the cookies yourself, but if you do you also need to consider the secureCookie prefix in next-auth:
|
Beta Was this translation helpful? Give feedback.
-
Plus one for the feature. It seems like a much needed feature for anyone using a backend different from their next-auth authentication to be able to logout users from server side on JWT expiry/ invalidation. |
Beta Was this translation helpful? Give feedback.
-
plus one for needing to be able to logout user server side. Using latest app dir. |
Beta Was this translation helpful? Give feedback.
-
can we just clear all of our cookies to logout? |
Beta Was this translation helpful? Give feedback.
-
I don't understand why it is so hard to do this. |
Beta Was this translation helpful? Give feedback.
-
Im also stuck on this, i even thought it exists, just realizing it doesnt |
Beta Was this translation helpful? Give feedback.
-
Docs or it didn’t happen
Sent from [Proton Mail](https://proton.me/mail/home) for iOS
…On Sun, May 5, 2024 at 3:02 AM, Sonfack Nelson Mandela ***@***.***(mailto:On Sun, May 5, 2024 at 3:02 AM, Sonfack Nelson Mandela <<a href=)> wrote:
Hey ***@***.***(https://github.com/CRIMSON-CORP) have you tried the latest version of next-auth now authjs ?
It's still in beta but it's now possible to sign out from the server.
—
Reply to this email directly, [view it on GitHub](#5334 (reply in thread)), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/AH3JIEUSX25IG3EJ4VHZPM3ZAX7TNAVCNFSM6AAAAAAQKS77CKVHI2DSMVQWIX3LMV43SRDJONRXK43TNFXW4Q3PNVWWK3TUHM4TGMJYHE4DI).
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
First step: |
Beta Was this translation helpful? Give feedback.
-
For me, using the supabase signout method works fine. I referenced this repository which goes over how to set up supabase auth and also has a logout function that works wonderfully: There should be no need to clear cookies manually. Hope it helps!! :) |
Beta Was this translation helpful? Give feedback.
-
Just adding this here as it might help someone.
I remembered I used the nextjs dashboard tutorial and it worked, then tried doing the same thing in another nextjs setup and I had a lot of issue. I tried several suggestions above that didn't work until I downgraded the next version from 14.0.3 to 14.02 |
Beta Was this translation helpful? Give feedback.
-
Hi there, I've spent quite a lot of time on this issue. Finally, the mix of server-side and client-side code works for me. First, return an error on the server side. Then, check it inside the client-side component.
|
Beta Was this translation helpful? Give feedback.
-
Based on a combination of the above suggestions as a hackish workaround to sign out the user if the access token has expired, we can set the JWT exp to match the access token exp. This can also be used to set the // auth.config.ts
// ...
async jwt({ token, user, account, trigger, session }) {
if (user) {
const { accessToken, ...userinfo } = user;
token.accessToken = accessToken;
token.exp = getTokenExp(accessToken!);
token.user = userinfo as User;
// auth.js v5 is not respecting the user.id we are setting in the profile cb result
// it is being overridden by the Oauth provider id
token.user.id = userinfo.user_id;
delete token.user.user_id;
if (account) {
token.provider = account?.provider;
}
token.scopes = getSecurityScopes(accessToken);
}
// ...
return token;
},
async session({ session, token }) {
const _session: Session = session;
if (token) {
if (session) {
_session.provider = token.provider;
_session.accessToken = token.accessToken;
_session.scopes = token.scopes;
_session.user = token.user;
_session.error = token.error;
_session.expires = new Date(token.exp * 1000).toISOString();
}
}
return _session;
}, // middleware.ts
const auth = NextAuth(authConfig).auth;
export default auth(async req => {
const pathname = req.nextUrl.pathname;
console.log('PATH:', pathname);
const authenticated = !!req.auth;
const isTokenExpired =
req.auth?.expires && new Date(req.auth.expires) < new Date();
// Handle authenticated redirects from auth pages
if (pathname.startsWith('/login') || pathname.startsWith('/register')) {
if (authenticated && !isTokenExpired)
return NextResponse.redirect(new URL(HOME_URL, req.url));
return NextResponse.next();
}
// Handle unauthenticated access
if (!authenticated) return redirectWithQuery(req, '/login');
if (isTokenExpired) return redirectWithQuery(req, '/login?signInAgain=true');
const scopes = req.auth?.scopes ?? [];
const isAuthorized = scopeValidator.isAuthorized(scopes, pathname);
// handle authorized redirects from signup page
if (pathname.startsWith('/signup')) {
if (isAuthorized) return NextResponse.redirect(new URL(HOME_URL, req.url));
return NextResponse.next();
}
// Check if path requires specific scopes
if (!isAuthorized) {
return NextResponse.redirect(new URL('/signup', req.url));
}
return NextResponse.next(); // on login page include:
'use client';
import { useSearchParams } from 'next/navigation';
import { signOut } from 'next-auth/react';
// ...
const searchParams = useSearchParams();
const signInAgain = searchParams?.get('signInAgain');
React.useEffect(() => {
if (signInAgain === 'true') {
signOut({ redirect: false });
}
}, [signInAgain]); The annoying thing is that we can't simply call 'use server';
import { auth, signIn, signOut } from '@/auth';
export async function getServerAuth() {
const session = await auth();
const user = session?.user as UserInfo | undefined;
const accessToken = session?.accessToken;
return { session, user, accessToken };
}
export async function getAuth(nextUrl?: string) {
const { session, user, accessToken } = await getServerAuth();
if (!session || !user || !accessToken) {
const redirectUrl = authConfig?.pages?.signIn || '/login';
const next = nextUrl ? `?next=${nextUrl}` : '';
redirect(`${redirectUrl}${next}`);
}
// here we could check if session.expires is in the past.
// call `signOut` if it is expired.
return { session, user, accessToken };
} |
Beta Was this translation helpful? Give feedback.
-
I am yet to write my own solution to this, but I am thinking about adding This solution doesn't involve extra fetch and per-page revalidation. I will write an update here once I am sure that my solution is working. |
Beta Was this translation helpful? Give feedback.
-
I have spent some time working on a solution and came up with 2 solutions. 1. If you need to revalidate session every X seconds/daysIn your authOptions, you can choose maxAge for your session. You don’t need any extra setup, user will be logged out automatically when session becomes invalid. const oneDay = 86400; // 1 day in seconds (60 * 60 * 24)
const sevenDays = 604800; // 1 week in seconds (60 * 60 * 24 * 7)
const authOptions: NextAuthOptions = {
session: {
strategy: 'jwt',
maxAge: sevenDays,
},
... This value can be accesses/checked in session callback: // in authOptions, session callback
async session({ session, token }) {
console.log(session.expires);
return session;
},
... 2. If you want to revalidate user sessions on-demandThe only way you can do this is on a jwt callback level, since it’s responsible for getting user data into session callback. The way I do it is as follows: 2.1 Create route.ts for fetching user data by user.id (ex. app/api/user/id/[id].route.ts// /app/api/user/[id]/route.ts
import prisma from '@/lib/db';
import { dictionary } from '@/utils/dictionary';
import { NextResponse } from 'next/server';
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
try {
const user = await prisma.user.findUnique({
where: {
id: params.id,
},
});
if (!user) {
console.log('No user found', params.id);
return NextResponse.json(null);
}
return NextResponse.json(user);
} catch (error) {
console.log(error);
NextResponse.json(
{ error: dictionary.general.getRequestError },
{ status: 500 }
);
}
} 2.2 create server action that fetch this route handler and cache it using next tag// /app/lib/user.ts
// This function is used in authOptions
"use server"
export async function getUserById(userId: string): Promise<UserFull | null> {
console.log('getUserById', userId);
const response = await fetch(`${env.siteUrl}/api/user/id/${userId}`, {
next: {
tags: ['user'],
revalidate: 604800, // 7 days in seconds (60 * 60 * 24 * 7)
},
method: 'GET',
});
const data = await response.json();
if (!response.ok) {
throw new Error('Failed get product information.');
}
return data;
} 2.3 Use created server action in jwt callback// in authOptions, jwt callback
async jwt({ token, user, trigger, session }) {
if (token.id) {
// This is called when user is already logged in (getUserId() is cached)
// Cache doesn't persist between deployments, meaning user will stay logged in between deployments, but getUserId() will result in data fetch (cache miss)
const userFull = await getUserById(token.id as string);
return userFull;
}
if (user) {
// This will only be executed upon signIn()
const userFull = await getUserById(user.id as string);
return userFull;
}
if (trigger === 'update') {
// Handle session update
return { ...token, ...session.user };
}
return token;
}, 2.4 Add revalidateTag() action// somewhere in your admin panel
revalidateTag('user'); This way I achieved on-demand user session revalidating without a need to log out users. For even more enchances caching, I usually add dynamic tag to fetch with userId. It can look like this: const response = await fetch(`${env.siteUrl}/api/user/id/${userId}`, {
next: {
tags: ['user', `userId-${userId}`],
revalidate: 604800, // 7 days in seconds (60 * 60 * 24 * 7)
},
method: 'GET',
}); This way I can choose whether to revalidate cache for all users with The only caveat of 2nd method is the fact that nextjs cache doesn’t persist between deployments, meaning auth flow is as follows:
This is not a big deal, especially considering that the entire rest of the data fetches will also be revalidated, not just user data fetch. I solve that by simply having 2 environments: stage and production. Deploys in prod are less frequent, so I get less cache miss'es. Hopefully that helped someone |
Beta Was this translation helpful? Give feedback.
-
Not exactly // auth/signout.ts|js
"use client";
import { signOut } from "next-auth/react";
import { useEffect } from "react";
export default function SignOut() {
useEffect(() => {
signOut({ redirect: true, callbackUrl: "/" }); # to home
}, []);
return null;
} // some-page.ts|js
export default async function Page() {
const session = await auth();
// Some code that can't refresh token
if (session?.error) {
return <SignOut />;
}
if (!session) {
return (
<div>
<p>Not authenticated</p>
</div>
);
} |
Beta Was this translation helpful? Give feedback.
-
it's almost the end of 2024 end we still don't have the solution for this. my application rely on this so much. |
Beta Was this translation helpful? Give feedback.
-
Question 💬
What would be considered proper way to sign out the user from the server side, like from
getServerSideProps
?Is it safe to just expire
next-auth.session-token
cookie (using name that is set under cookies sessionToken options)? I am aware this would not triggersignOut
event.Sending POST request to
/api/auth/signout
(with csrfToken) does not produce expected result from server side. Id does work fine when called from client.How to reproduce ☕️
Simple question. No code needed.
Contributing 🙌🏽
No, I am afraid I cannot help regarding this
Beta Was this translation helpful? Give feedback.
All reactions