-
-
Notifications
You must be signed in to change notification settings - Fork 19
Description
Bug report
- I confirm this is a bug with Supabase, not with my own application.
- I confirm I have searched the Docs, GitHub Discussions, and Discord.
Describe the bug
Hi everyone, after long search around GitHub issues, Reddit threads, Supabase Support, and speaking with David Lorenz (the writer of the Supabase book), I'd like this issue to be addressed here. My NextJS app deployed to Vercel is constantly getting an [AuthApiError]: Invalid Refresh Token: Session Expired error, and my assumption is that it logs the user out of the app. This is hard for me to reproduce, and I've even diluted my middleware to least it could be since I used both getUser and getSession for some time since I needed access to JWT.
The reason I've removed the getSession is that everyone seems to find an issue in that, and it is not. And btw if I really need access to JWT within middleware because of custom claims, either add it to getUser or expand the docs.
I'll add the code to my middleware in a gist here:
import { type NextRequest } from 'next/server';
import { updateSession } from '@/utils/middleware/authorization';
export async function middleware(request: NextRequest) {
return await updateSession(request);
}
export const config = {
matcher: [
// Protected Routes (authorized users only)
'/account-settings/:path*',
'/analytics/:path*',
'/event-editor/:path*',
'/my-events/:path*',
'/my-tickets/:path*',
'/wishlist/:path*',
// Auth Routes (unauthorized users only)
'/login',
'/register',
'/forgot-password',
'/reset-password'
]
};
import { Routes } from '@/types';
import { NextRequest, NextResponse } from 'next/server';
import { createServerClient } from '@supabase/ssr';
const authenticationRoutes = [
Routes.LOGIN,
Routes.REGISTER,
Routes.FORGOT_PASSWORD,
];
const authorizedRoutes = [
Routes.MY_EVENTS,
Routes.MY_TICKETS,
Routes.WISHLIST,
Routes.ACCOUNT_SETTINGS,
Routes.EVENT_EDITOR,
Routes.ANALYTICS,
];
export const updateSession = async (request: NextRequest) => {
let supabaseResponse = NextResponse.next({ request });
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return request.cookies.getAll()
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))
supabaseResponse = NextResponse.next({ request });
cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options))
},
},
}
);
// This is needed to check if the user is who they say they are by making a request to the supabase auth service which is a lightweight edge service
const { data: { user } } = await supabase.auth.getUser();
// If the user is not logged in, we need to check if they are trying to access an authorized route
if (!user) {
const isAuthorizedRoute = authorizedRoutes.some((route) =>
request.nextUrl.pathname.startsWith(route)
);
if (isAuthorizedRoute) {
return redirectTo(Routes.LOGIN, request);
}
// If the user is not trying to access an authorized route, we can proceed
return supabaseResponse;
}
// If the user is logged in, we need to check if they are trying to access an authorized route
if (user) {
const isAuthenticationRoute = authenticationRoutes.some((route) =>
request.nextUrl.pathname.startsWith(route)
);
// If the user is trying to access an authentication route, we need to redirect them to the home page
if (isAuthenticationRoute) {
return redirectTo(Routes.HOME, request);
}
return supabaseResponse;
}
};
const redirectTo = ( destination: string, request: NextRequest ) => {
const url = request.nextUrl.clone()
url.pathname = destination
return NextResponse.redirect(url)
}
IMPORTANT: that I'm using getUser within several different places within the app to make sure if a logged in user accesses the site.
IMPORTANT: I solely rely on middleware for auth after loging in / signing up. There is no client instance used anywhere in the app.
To Reproduce
I really have no idea how to reproduce it. The refresh token works fine in local env.
Expected behavior
The middleware should be responsible for updating the token based on refresh token, and if the token is really expired, the error is possibly unneeded since it is a normal behaviour (might be wrong about this one).
Screenshots
System information
- OS: [e.g. macOS, Windows]
- Browser (if applies) [e.g. chrome, safari]
- Version of supabase-ssr:
'@supabase/[email protected]':
resolution: {integrity: sha512-n3plRhr2Bs8Xun1o4S3k1CDv17iH5QY9YcoEvXX3bxV1/5XSasA0mNXYycFmADIdtdE6BG9MRjP5CGIs8qxC8A==}
peerDependencies:
'@supabase/supabase-js': ^2.43.4
'@supabase/[email protected]':
resolution: {integrity: sha512-E5p8/zOLaQ3a462MZnmnz03CrduA5ySH9hZyL03Y+QZLIOO4/Gs8Rdy4ZCKDHsN7x0xdanVEWWFN3pJFQr9/hg==}
- Version of Node.js: [e.g. 20]
