redirect() not working from inside server api route
Answered
Cinnamon posted this in #help-forum
CinnamonOP
Most of my api routes call this
the output shows the log statement, but no redirect is happening
getCurrentUser method. if the cookie is expired, it's should redirect the user to the sign-in page. This was working but now its stopped working (didnt make any changes to it) export async function getCurrentUser() {
await initAdmin();
const auth = getAuth();
const session = await getSession();
if (!(await isUserAuthenticated(session))) {
console.log('++ Not authenticated')
redirect('/sign-in');
}
const decodedIdToken = await auth.verifySessionCookie(session!);
const currentUser = await auth.getUser(decodedIdToken.uid);
return currentUser;
}the output shows the log statement, but no redirect is happening
errorInfo: {
code: 'auth/session-cookie-expired',
message: 'Firebase session cookie has expired. Get a fresh session cookie from your client app and try again (auth/session-cookie-expired). See https://firebase.google.com/docs/auth/admin/manage-cookies for details on how to retrieve a session cookie.'
},
codePrefix: 'auth'
}
++ Not authenticated
POST /api/canon/create 307 in 312ms
✓ Compiled /sign-in in 302ms (1999 modules)
Registered firebase app.
POST /sign-in 200 in 581msAnswered by joulev
for the case of the api route, it is basically like this
you do
fetch("/api/foo")
in the api route, redirect("/hello")
nextjs caught the redirect() call, producing a redirect response to "/hello" for that api route
your fetch then becomes equivalent to
fetch("/hello")
and as you already know, fetch("/hello") doesn't redirect anyone to anything. you simply get the html of /hello.
you do
fetch("/api/foo")
in the api route, redirect("/hello")
nextjs caught the redirect() call, producing a redirect response to "/hello" for that api route
your fetch then becomes equivalent to
fetch("/hello")
and as you already know, fetch("/hello") doesn't redirect anyone to anything. you simply get the html of /hello.
12 Replies
CinnamonOP
GetCurrentUser is a function inside lib. It’s being called from insider a server api route
@Cinnamon GetCurrentUser is a function inside lib. It’s being called from insider a server api route
can i see how you used it in one of the api route?
CinnamonOP
for sure, here's /api/canon/create/route.ts
// src/app/api/canon/create/route.ts
import { getCurrentUser } from '@/lib/firebase/server/auth';
import { initAdmin } from '@/lib/firebase/server/init';
import { Canon } from '@/lib/models/Canon';
import { FieldValue, getFirestore } from 'firebase-admin/firestore';
import { NextRequest, NextResponse } from 'next/server';
// import { APIResponse } from "@/types";
type CreateTeamRequestBody = {
teamId: string;
canon: Canon
};
export async function POST(request: NextRequest) {
const user = await getCurrentUser();
if (!user) {
return NextResponse.json({
success: false,
data: 'User not authenticated.'
});
}
const { uid } = user;
const reqBody = (await request.json()) as CreateTeamRequestBody;
const { teamId, canon } = reqBody;
await initAdmin();
const firestore = getFirestore();
const canonRef = firestore.collection('canons');
const response = await canonRef.add({
...canon,
teamId,
createdBy: uid,
visibleTo: [teamId],
createdAt: FieldValue.serverTimestamp(),
updatedAt: FieldValue.serverTimestamp()
});
console.log('Canon created with ID: ', response.id);
return NextResponse.json<any>({
success: true,
data: {canonId: response.id}
});
}thats odd
@Cinnamon Most of my api routes call this `getCurrentUser` method. if the cookie is expired, it's should redirect the user to the sign-in page. This was working but now its stopped working (didnt make any changes to it)
export async function getCurrentUser() {
await initAdmin();
const auth = getAuth();
const session = await getSession();
if (!(await isUserAuthenticated(session))) {
console.log('++ Not authenticated')
redirect('/sign-in');
}
const decodedIdToken = await auth.verifySessionCookie(session!);
const currentUser = await auth.getUser(decodedIdToken.uid);
return currentUser;
}
the output shows the log statement, but no redirect is happening
errorInfo: {
code: 'auth/session-cookie-expired',
message: 'Firebase session cookie has expired. Get a fresh session cookie from your client app and try again (auth/session-cookie-expired). See https://firebase.google.com/docs/auth/admin/manage-cookies for details on how to retrieve a session cookie.'
},
codePrefix: 'auth'
}
++ Not authenticated
POST /api/canon/create 307 in 312ms
✓ Compiled /sign-in in 302ms (1999 modules)
Registered firebase app.
POST /sign-in 200 in 581ms
redirect() doesn't work* in api routes/route handlers. it only works in
* server components
* client components**
* server actions
* it does work but not in the way you expect it to
** only in certain circumstances
* server components
* client components**
* server actions
* it does work but not in the way you expect it to
** only in certain circumstances
CinnamonOP
I see, can you explain the ways in which it works unexpectedly?
Is there a recommended way of triggering a redirect? I prefer not to have to handle authentication errors back inside every api call inside my app. Middleware recommended for that?
for the case of the api route, it is basically like this
you do
fetch("/api/foo")
in the api route, redirect("/hello")
nextjs caught the redirect() call, producing a redirect response to "/hello" for that api route
your fetch then becomes equivalent to
fetch("/hello")
and as you already know, fetch("/hello") doesn't redirect anyone to anything. you simply get the html of /hello.
you do
fetch("/api/foo")
in the api route, redirect("/hello")
nextjs caught the redirect() call, producing a redirect response to "/hello" for that api route
your fetch then becomes equivalent to
fetch("/hello")
and as you already know, fetch("/hello") doesn't redirect anyone to anything. you simply get the html of /hello.
Answer
if you go to /api/foo in the browser directly, you will be redirected to /hello normally
but here you fetch() it rather than going by the browser url bar, so the redirect doesn't happen in the frontend
but here you fetch() it rather than going by the browser url bar, so the redirect doesn't happen in the frontend
CinnamonOP
Thanks everyone! Much appreciated 🙏
@Cinnamon Is there a recommended way of triggering a redirect? I prefer not to have to handle authentication errors back inside every api call inside my app. Middleware recommended for that?
for this one i prefer middleware yep. or checking auth in layout (but beware that the method of checking auth in layout has a [gotcha](https://github.com/eric-burel/securing-rsc-layout-leak) to be mindful of)
if you want to redirect after the user does something, use server action. redirect() calls in server actions will be passed to the frontend router which will handle the redirect normally
if you want to redirect after the user does something, use server action. redirect() calls in server actions will be passed to the frontend router which will handle the redirect normally