Login redirect callback issue
Unanswered
Hassib posted this in #help-forum
HassibOP
I'm facing a weird issue when logging in, but it happens only when the app is deployed. It's working fine when I'm testing it on localhost.
I'm using AzureAD to log in and then making a request to get the exchange token for the backend, nothing is undefined. When trying to hit the login button, it gets the backend exchange token, and logs the user in and redirects user to the dashboard page which is this route "/". But whenever I deploy it, the login button works, but this happens:
- Assume the app URL is
The login route is
The dashboard route is
I'm using
Code is in the comments
I'm using AzureAD to log in and then making a request to get the exchange token for the backend, nothing is undefined. When trying to hit the login button, it gets the backend exchange token, and logs the user in and redirects user to the dashboard page which is this route "/". But whenever I deploy it, the login button works, but this happens:
- Assume the app URL is
example.com, when user hits login, the app URL becomes example.com/?callbackUrl=https%3A%2F%2Fexample.com%2F and trying again doesn't make a difference.The login route is
/loginThe dashboard route is
/I'm using
next-auth.Code is in the comments
6 Replies
HassibOP
import NextAuth from 'next-auth';
import AzureADProvider from 'next-auth/providers/azure-ad';
import axios from 'axios';
import { jwtDecode } from 'jwt-decode';export const authOptions = {
providers: [
AzureADProvider({
id: 'azure-ad',
clientId: process.env.AZURE_AD_CLIENT_ID,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
tenantId: process.env.AZURE_AD_TENANT_ID,
name: 'Login with Al Jazeera Media Network',
providerLogoPath: `${process.env.NEXTAUTH_URL}/assets/ajsvgs/al_jazeera.svg`,
style: {
logo: `${process.env.NEXTAUTH_URL}/assets/ajsvgs/al_jazeera.svg`
},
profile(profile) {
return {
id: profile.oid || profile.sub,
name: profile.name,
email: profile.email
};
}
})
],callbacks: {
async jwt({ token, account }) {
// Only perform token exchange during initial sign-in
if (account?.access_token) {
try {
const res = await axios.get(
`${process.env.NEXT_PUBLIC_BACKEND_SERVER_BASE_URL}/sanad-token-exchange/`,
{
headers: {
Authorization: `Bearer ${account.access_token}`,
secretkey: process.env.SANAD_SECRET_KEY || ''
}
}
);
if (res.data?.success) {
token.backendToken = res.data.access_token;
token.refresh_token = res.data.refresh_token;
// Decode the backend token to get expiration info
const decoded = jwtDecode(token.backendToken);
token.expt = decoded.exp;
token.ist = decoded.iat;
}
} catch (error) {
console.error('Token exchange failed:', error.message);
// Don't fail the authentication, just log the error
}
}
return token;
},
async session({ session, token }) {
// Send backend token and related info to the client
session.backendToken = token.backendToken;
session.refresh_token = token.refresh_token;
session.expt = token.expt;
session.ist = token.ist;
return session;
}
},pages: {
signIn: '/login',
signOut: '/'
},
secret: process.env.NEXTAUTH_SECRET,
debug: false
};
export default NextAuth(authOptions);Middleware
import { NextMiddleware, NextRequest, NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt';
export const middleware: NextMiddleware = async (req: NextRequest) => {
// Remove x-middleware-prefetch header to prevent VIP's infrastructure
// from caching empty JSON responses on prefetched data for SSR pages. See the following URLs for more info:
// https://github.com/vercel/next.js/discussions/45997
// https://github.com/vercel/next.js/pull/45772
// https://github.com/vercel/next.js/blob/v13.1.1/packages/next/server/base-server.ts#L1069
const headers = new Headers(req.headers);
headers.delete('x-middleware-prefetch');
// Required health check endpoint on VIP. Do not remove.
if (req.nextUrl.pathname === '/cache-healthcheck') {
return NextResponse.json({ message: 'ok' }, { status: 200 });
}
// Authentication checks
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
const { pathname } = req.nextUrl;
// Public routes that don't require authentication
const publicRoutes = ['/login', '/api/auth'];
const isPublicRoute = publicRoutes.some((route) =>
pathname.startsWith(route)
);
// If user is not authenticated and trying to access protected route
if (!token && !isPublicRoute && pathname !== '/') {
const loginUrl = new URL('/login', req.url);
return NextResponse.redirect(loginUrl);
}
// If user is authenticated and trying to access login page, redirect to home
if (token && pathname === '/login') {
const homeUrl = new URL('/', req.url);
return NextResponse.redirect(homeUrl);
}
// Continue as normal through the Next.js lifecycle.
return NextResponse.next({
request: {
headers
}
});
};export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api/auth (auth API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* - public folder
*/
'/((?!api/auth|_next/static|_next/image|favicon.ico|public).*)'
]
};