Unable to retrieve Authjs (NextAuth 5) access token server-side in Next.js 15 with Django backend

I'm trying to set up authentication in my Next.js application using NextAuth.js with a Django backend. As Oauth provider i'm use django-oauth-toolkit.

The goal is to pass the access token in the Authorization header when making requests to the Django API.

I have successfully set up the OAuth2 provider in Auth.js, and I'm able to log in through the Django authentication system.

However, when I try to retrieve the access token server-side in my Next.js app (in a server component), I am unable to find it.

I try to follow the official tutorial for thirdy parts backends: https://github.com/nextauthjs/next-auth-example

I'm using NextJS 15.1.7 and Authjs (NextAuth.js v5)

Here’s the relevant code:

src\auth.ts

import NextAuth from "next-auth"
import "next-auth/jwt"

declare module "next-auth" {
  interface Session {
    accessToken?: string
  }
}

declare module "next-auth/jwt" {
  interface JWT {
    accessToken?: string
  }
}

const DJANGO_URL = process.env.NEXT_PUBLIC_DJANGO_URL;
const NEXTJS_URL = process.env.NEXT_PUBLIC_NEXTJS_URL;

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [{
    id: "django", // signIn("my-provider") and will be part of the callback URL
    name: "Django", // optional, used on the default login page as the button text.
    type: "oauth", // or "oauth" for OAuth 2 providers
    userinfo: `${DJANGO_URL}/api/o/userinfo/`,
    token: `${DJANGO_URL}/api/o/token/`,
    wellKnown: `${DJANGO_URL}/api/o/.well-known/openid-configuration/`,
    authorization: {
      params: {
        prompt: "login",
        scope: "openid read write",
        redirect_uri: `${NEXTJS_URL}/api/auth/callback/django`,
      },
    },
    issuer: `${DJANGO_URL}/api/o`, // to infer the .well-known/openid-configuration URL
    clientId: process.env.DJANGO_ID,
    clientSecret: process.env.DJANGO_SECRET,
    async profile(profile) {
      return{
        id: profile.sub,
        email: profile.email,
      }
    },
  }],
  pages:{
    signIn: "/login",
  },
  session: { strategy: "jwt" },
  callbacks: {
    authorized: async ({ auth }) => {
      // Logged in users are authenticated, otherwise redirect to login page
      return !!auth
    },
    jwt({ token, trigger, session, account }) {
      if (trigger === "update") token.name = session.user.name
      if (account?.provider === "django") {
        return { ...token, 
                 accessToken: account.access_token 
               }
      }
      return token
    },
    async session({ session, token }) {
      if (token?.accessToken) session.accessToken = token.accessToken

      return session
    },
  },
})

and then i set up a proxy as the tutorial, so every request that starts with "/api/" will be redirected to the backend with the access token in the headers, so:

src\app\api\ [...proxy]\route.ts

import { auth } from "@/auth"
import { NextRequest, NextResponse } from "next/server"

async function handler(request: NextRequest) {
  console.log("📢 Route Handler activated!")

  const session = await auth()
  console.log("🧐 Session:", session)

  if (!session?.accessToken) {
    console.log("⛔ No token in session.");
    return NextResponse.json("Error Unauthorized 401")
  }

  const headers = new Headers(request.headers)
  headers.set("Authorization", `Bearer ${session?.accessToken}`)
  headers.set("Accept", "application/json")

  let backendUrl = "https://localhost:8000/"

  let url = request.nextUrl.href.replace(request.nextUrl.origin, backendUrl)
  let result = await fetch(url, { 
    headers, body: 
    request.body,
    credentials: "include"
   })

  let data = await result.json();

  return NextResponse.json(data)
}

export const dynamic = "force-dynamic"

export { handler as GET, handler as POST }

and then the server-side component, simple as possible:

src\components\usermenu.tsx

async function getUserData() {
  const res = await fetch("https://localhost:3000/api/users/customuser/me/"); //request handled by the [...proxy] 

  if (!res.ok) {
    throw new Error("Error retrieving user data");
  }

  return res.json();
}

export default async function UserProfilePage() {
  const userData = await getUserData();

  return (
    <div>
      {JSON.stringify(userData, null, 2)}
    </div>
  );
}

However, when the request is handled server-side (in the API route handler or server component), the token is not available. I’ve tried logging the token and checking cookies, but nothing seems to work.

I'm using HTTPS locally for both my Next.js and Django apps, and I created my SSL certificates manually.

My questions are:

  • How can I correctly retrieve the NextAuth access token server-side in my Next.js application?
  • Is there a specific way I need to configure my session or cookies to make sure the token is available server-side?
  • Could the issue be related to the SSL certificates I'm using locally, and how can I resolve that if so?
  • Is there something wrong with how I’m using auth() or accessing the session in my Next.js app?

Thank you so much for your time and help. I truly appreciate your expertise and assistance in resolving this issue! I can't thank you enough for your support. Your insights have been invaluable, and I'm grateful for your effort in guiding me through this!

Вернуться на верх