nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.61k stars 3.46k forks source link

How do I access my access_token in a server component? #7913

Closed jakobpucher closed 1 year ago

jakobpucher commented 1 year ago

Question 💬

I'm logging in using NextAuth with Azure AD as the provider, which works fine.

In the NextAuth callbacks, I receive the access_token. In middleware.ts, I can use the getToken(req, secret) method and access the access_token (but what to do with it there?). In my internal API routes, the getToken(req, secret) always returns null.

I read in another question thread that this is the expected behaviour. These values are not passed to the API routes for security reasons.

NOTE: The only reasons i created the internal API routes was because there I got access to the request object which is needed to use the getToken() method

My question then becomes:

How do I access my JWT token in my server components? And if they are encrypted by next, how do I decode and use them?

My NEXTAUTH_SECRET is created with openssl rand -base64 32 (I dont know if you should say that in public )

How to reproduce ☕️

auth.ts

import { NextAuthOptions } from "next-auth";
import AzureADProvider from "next-auth/providers/azure-ad";
import { CLIENT_ID, CLIENT_SECRET, TENANT_ID } from "./utils/envPublic";

const authOptions: NextAuthOptions = {
    providers: [
        AzureADProvider({
            clientId: CLIENT_ID,
            clientSecret: CLIENT_SECRET,
            tenantId: TENANT_ID,
            authorization: {
                params: {
                    scope: "openid profile email api://myapi/my.scope",
                }
            }
        })
    ],
    secret: process.env.NEXTAUTH_SECRET,
    session: {
        strategy: "jwt"
    },
    jwt: {
        secret: process.env.NEXTAUTH_SECRET
    },
    callbacks: {
        async jwt({ token, account, user }) {
            token.accessToken = account?.access_token;
            return { ...token, ...user, ...account };
        },
        async session({ session, token, user }) {
            session.token = token.accessToken;
            session.user.token = token.accessToken;
            return session;
        },
        async redirect() {
            return "/"
        }
    },

};

export default authOptions;

My server component:

const Home = async () => {
  // How do I get the access token here?
  let response = await fetch("http://external.api.that.needs.accestoken.to.work/");
  let json = await response.json();

  return (
    <div className="flex flex-col items-center">
      <p className="p-12 hover:bg-red-600 cursor-pointer">Hello there github friends</p>
    </div>
  )
};

export default Home;

I've also done some attempts to extend the types/interfaces to store the access_token in the callbacks. Tbh, im not really sure how this works. next-auth-d.ts:

import NextAuth, { DefaultSession } from "next-auth";
import type { Session, Account } from "next-auth";

declare module "next-auth" {

  interface Session {
    token?: accessToken;
    user: {
      token?: accessToken;
    } & DefaultSession["user"];
  }
  interface Account {
    access_token: accessToken;
    user: {
      token?: accessToken;
    } & DefaultSession["user"];
  }
}

Contributing 🙌🏽

Yes, I am willing to help answer this question in a PR

jakobpucher commented 1 year ago

I tried to get the token from cookies() in my server components and log them. 2 tokens get logged: next-auth.session-token.0 next-auth.session-token.1

I must've been doing something wrong in the callbacks to store 2 of them. However, both signatures are invalid according to jwt.io when i copy them there.

import { cookies } from "next/dist/client/components/headers";

const Home = async () => {

  const cooks = cookies();
  const allCookies = cooks.getAll();
  allCookies.forEach((cookie) => {
    console.log(cookie.name);
    console.log(cookie.value);
  });

  return (
    <div className="flex flex-col items-center">
      <p className="p-12 hover:bg-red-600 cursor-pointer">Hello there github friends</p>
    </div>
  )
};

export default Home;
balazsorban44 commented 1 year ago

check out the docs https://next-auth.js.org/configuration/nextjs#in-app-directory

Kevin-McGonigle commented 1 year ago

The docs don't really cover this question (as far as I can see). They show how to get the session, sure, but not how to get the access token in a server component. getToken works fine for route handlers, but not for server components since there's no req to pass in. I think this should be easy enough to implement though, taking an approach similar to getServerSession - I'll give it a go!

stayclaxxy commented 10 months ago

Sorry to open this thread back up - I am in the same exact scenario currently and have not found a viable solution. I would prefer to not have to access the cookies to extract the JWT within the server component. Has anyone been able to find a way to get this done? On my client component it returns all of the properties i've defined in the session callback, but in the server component, getServerSession only returns the user object with name, email, image properties and nothing else. I am using the AzureB2C provider.

joonasjarvinen92 commented 10 months ago

I managed to get the access_token by creating a callback in the authOption object:

// app/api/auth/[...nextauth]/route.ts

const authOptions: NextAuthOptions = {
    providers: [
        AzureADProvider({
            clientId: process.env.AZURE_AD_CLIENT_ID ?? '',
            clientSecret: process.env.AZURE_AD_CLIENT_SECRET ?? '',
            tenantId: process.env.AZURE_AD_TENANT_ID,
        }),
    ],
    session: {
        strategy: 'jwt',  // <-- make sure to use jwt here
        maxAge: 30 * 24 * 60 * 60,
    },
    callbacks: {
        jwt: async ({ token, user, account }) => {
            if (account && account.access_token) {
                token.accessToken = account.access_token // <-- adding the access_token here
            }
            return token
        },
    },
    secret: process.env.NEXTAUTH_SECRET,
}

const handler = NextAuth(authOptions)

export { handler as GET, handler as POST }

Now you should be able to see the accessToken when calling getToken()

// app/api/test/route.ts

import { NextRequest, NextResponse } from 'next/server'
import { getToken } from 'next-auth/jwt'

export async function GET(req: NextRequest) {
    const token = await getToken({
        req,
        secret: process.env.NEXTAUTH_SECRET ?? '',
    })

    console.log('token.accessToken: ', token?.accessToken)

    return NextResponse.json({})
}
Yazan-Ali-01 commented 10 months ago

const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET ?? '', })

did you manage anyway to solve this especially that we cant modify the server session and that it is always having only name, email, image only

raulclaudino commented 10 months ago
import { getToken } from 'next-auth/jwt';
export async function PATCH(req: NextRequest, route: { params: RouteParams }) {
    const token: any = await getToken({ req, raw: true }); /* eyJhbGciOiJkaXIiLCJlbmMiOiJBM... */
}
raulclaudino commented 10 months ago

const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET ?? '', })

did you manage anyway to solve this especially that we cant modify the server session and that it is always having only name, email, image only

const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET ?? '', })

did you manage anyway to solve this especially that we cant modify the server session and that it is always having only name, email, image only

@Yazan-Ali-01 https://next-auth.js.org/getting-started/client#updating-the-session

Yazan-Ali-01 commented 10 months ago

const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET ?? '', })

did you manage anyway to solve this especially that we cant modify the server session and that it is always having only name, email, image only

const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET ?? '', })

did you manage anyway to solve this especially that we cant modify the server session and that it is always having only name, email, image only

@Yazan-Ali-01 https://next-auth.js.org/getting-started/client#updating-the-session

this would update the server session or the client session ?? also where are you using this code import { getToken } from 'next-auth/jwt'; export async function PATCH(req: NextRequest, route: { params: RouteParams }) { const token: any = await getToken({ req, raw: true }); /* eyJhbGciOiJkaXIiLCJlbmMiOiJBM... */ } is it inside middleware.js ?? and can you share your [...nextauth]/route.js file please ?? maybe the callbacks are the problem for me thanks

joonasjarvinen92 commented 9 months ago

I have made some changes, so my /app/api/auth/[...nextauth]/route.ts looks like this:

import NextAuth from 'next-auth/next'
import { authOptions } from '@/auth'

const handler = NextAuth(authOptions)

export { handler as GET, handler as POST }

the authOptions coming from /auth.ts is shown below. Do note that I added the session callback along with the jwt callback, so the jwt can be accessed from React components

// /auth.ts

import {
    GetServerSidePropsContext,
    NextApiRequest,
    NextApiResponse,
} from 'next'
import { NextAuthOptions, getServerSession } from 'next-auth'
import AzureADProvider from 'next-auth/providers/azure-ad'

const authOptions: NextAuthOptions = {
    providers: [
        AzureADProvider({
            clientId: process.env.AZURE_AD_CLIENT_ID ?? '',
            clientSecret: process.env.AZURE_AD_CLIENT_SECRET ?? '',
            tenantId: process.env.AZURE_AD_TENANT_ID,
        }),
    ],
    session: {
        strategy: 'jwt',
        maxAge: 30 * 24 * 60 * 60, // 30 days
    },
    callbacks: {
        jwt: async ({ token, user, account }) => {
            if (account && account.access_token) {
                // set access_token to the token payload
                token.accessToken = account.access_token
            }

            return token
        },
        redirect: async ({ url, baseUrl }) => {
            return baseUrl
        },
        session: async ({ session, token, user }) => {
            // If we want to make the accessToken available in components, then we have to explicitly forward it here.
            return { ...session, token: token.accessToken }
        },
    },
    pages: {
        signIn: '/',
    },
    secret: process.env.NEXTAUTH_SECRET,
}

function auth(  // <-- use this function to access the jwt from React components
    ...args:
        | [GetServerSidePropsContext['req'], GetServerSidePropsContext['res']]
        | [NextApiRequest, NextApiResponse]
        | []
) {
    return getServerSession(...args, authOptions)
}

export { authOptions, auth }

You can now import the auth function from above, and use it inside components

// /app/[locale]/(auth)/dashboard/overview/page.tsx

import { auth } from '@/auth'

export default async function Page() {
    const session = await auth()
    console.log('jwt: ', session?.token)
    return <></>
}
linus-jansson commented 9 months ago
callbacks: {
    jwt: async ({ token, user, account }) => {
        if (account && account.access_token) {
            // set access_token to the token payload
            token.accessToken = account.access_token
        }

        return token
    },
    redirect: async ({ url, baseUrl }) => {
        return baseUrl
    },
    session: async ({ session, token, user }) => {
        // If we want to make the accessToken available in components, then we have to explicitly forward it here.
        return { ...session, token: token.accessToken }
    },
},

This works! But have you made use of refresh token rotation? My IdentityProvider puts an short expire time on the token so its useless after 5min, and next auth doesn't automatically refresh the access token when the it expires.

Feels like its something that should be handle automatically as long as the session is still valid.

SRTigers98 commented 8 months ago

@linus-jansson I had the same problem with the refresh token rotation and solved it by adapting the jwt callback. I used the code from the refresh token rotation guide. Additionally, since the callback is only called when the session is created or updated (described in the jwt callback documentation), I created a middleware that calls the getServerSession function to make sure the access token is always up to date when an api function is called.

I hope this helps with your problem.

abdushkur commented 8 months ago
import { cookies } from 'next/headers'

export default function Page() {
  const cookieStore = cookies()
  const theme = cookieStore.get('theme')
  return '...'
}
avillegastorres commented 8 months ago

@linus-jansson I had the same problem with the refresh token rotation and solved it by adapting the jwt callback. I used the code from the refresh token rotation guide. Additionally, since the callback is only called when the session is created or updated (described in the jwt callback documentation), I created a middleware that calls the getServerSession function to make sure the access token is always up to date when an api function is called.

I hope this helps with your problem.

Do you have a snippet with the example? Thanks!

SRTigers98 commented 8 months ago

@avillegastorres I used the following code for the jwt callback to refresh the token issued by a keycloak server:

// ...
jwt: async ({ token, account }) => {
  if (account) {
    return {
      accessToken: account.access_token || "",
      expiresAt: account.expires_at || 0,
      refreshToken: account.refresh_token || "",
    };
  }

  if (Date.now() < token.expiresAt * 1000) {
    return token;
  }

  return await refreshToken(token);
},
// ...

async function refreshToken(token: JWT): Promise<JWT> {
  try {
    const tokenEndpoint = `${
      oauthHost + oauthIssuer
    }/protocol/openid-connect/token`;

    // https://nuxt.com/docs/getting-started/data-fetching#fetch
    const response = await $fetch<TokenSet>(tokenEndpoint, {
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: new URLSearchParams({
        client_id: oauthClient,
        client_secret: oauthSecret,
        grant_type: "refresh_token",
        refresh_token: token.refreshToken,
      }),
      method: "POST",
    });

    return {
      ...token,
      accessToken: response.access_token || "",
      expiresAt: response.expires_at || 0,
      refreshToken: response.refresh_token ?? token.refreshToken,
    };
  } catch (error) {
    console.error("Error refreshing access token", error);
    return { ...token, error: "RefreshAccessTokenError" as const };
  }
}

declare module "next-auth/jwt" {
  interface JWT {
    accessToken: string;
    expiresAt: number;
    refreshToken: string;
    error?: "RefreshAccessTokenError";
  }
}

I also implemented a middleware to call the getServerSession function:

// https://nuxt.com/docs/guide/directory-structure/server#server-middleware
export default defineEventHandler(async (event) => {
  // Trigger JWT callback to refresh token if needed
  await getServerSession(event);
});

For my project I used Nuxt 3 with the Sidebase Auth integration which uses next-auth@4.22.5, so some things may need to be adjusted.

Nidroide commented 8 months ago
...
        session: async ({ session, token, user }) => {
            // If we want to make the accessToken available in components, then we have to explicitly forward it here.
            return { ...session, token: token.accessToken }
...

Be aware that including the access token in the session will also expose it to the client (getSession, useSession...).

avillegastorres commented 8 months ago

Hey guys, I did a proxy API route on my next.js API folder that brings me the access token. My code looks like this.

//This file lives in src/app/api/proxy/route.ts
import { getToken } from "next-auth/jwt";
import { NextRequest, NextResponse } from "next/server";

export async function PATCH(req: NextRequest) {
  const token = await getToken({
    req,
  });

  if (!token) {
    return NextResponse.json(
      {
        message: "Unauthorized",
      },
      {
        status: 401,
      }
    );
  }

//The "access_toke" name is up to you; you need to check how you pass it on the JWT callback
  return Response.json({ accessToken: token["access_token"] });
}

Now, on my server component, I'm calling it like this:

// src/app/page.tsx
import { getServerSession } from "next-auth";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import Landing from "./components/Landing";
import { Locale } from "./lib/i18n/dictionaries/types";

async function IndexPage({ params: { lang } }: { params: { lang: Locale } }) {
  const session = await getServerSession();

  const token = await fetch("http://localhost:3000/api/proxy", {
    method: "PATCH",
    headers: headers(), // this is important without, the API will not be able to extract the token from the header
  });

  console.log(await token.json());

  if (!session || !session.user) {
    redirect("/login");
  }

  return <Landing lang={lang} />;
}

export default IndexPage;

why I choose this

Ok, I decided to do it like this because I want to keep the access token information private from the client; the @joonasjarvinen92 works great if you don't mind that, but in my case, it wasn't an option. I hope the Next Auth introduces a better way to do this; in the meantime, I'll use this.

Another note is that you still need the JWT callback for this to work since it is the only way to add the access token into the JWT that NextAuth creates as part of the JWT strategy.

References:

benedwards44 commented 8 months ago

Hey guys, I did a proxy API route on my next.js API folder that brings me the access token. My code looks like this.

//This file lives in src/app/api/proxy/route.ts
import { getToken } from "next-auth/jwt";
import { NextRequest, NextResponse } from "next/server";

export async function PATCH(req: NextRequest) {
  const token = await getToken({
    req,
  });

  if (!token) {
    return NextResponse.json(
      {
        message: "Unauthorized",
      },
      {
        status: 401,
      }
    );
  }

//The "access_toke" name is up to you; you need to check how you pass it on the JWT callback
  return Response.json({ accessToken: token["access_token"] });
}

Now, on my server component, I'm calling it like this:

// src/app/page.tsx
import { getServerSession } from "next-auth";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import Landing from "./components/Landing";
import { Locale } from "./lib/i18n/dictionaries/types";

async function IndexPage({ params: { lang } }: { params: { lang: Locale } }) {
  const session = await getServerSession();

  const token = await fetch("http://localhost:3000/api/proxy", {
    method: "PATCH",
    headers: headers(), // this is important without, the API will not be able to extract the token from the header
  });

  console.log(await token.json());

  if (!session || !session.user) {
    redirect("/login");
  }

  return <Landing lang={lang} />;
}

export default IndexPage;

why I choose this

Ok, I decided to do it like this because I want to keep the access token information private from the client; the @joonasjarvinen92 works great if you don't mind that, but in my case, it wasn't an option. I hope the Next Auth introduces a better way to do this; in the meantime, I'll use this.

Another note is that you still need the JWT callback for this to work since it is the only way to add the access token into the JWT that NextAuth creates as part of the JWT strategy.

References:

@avillegastorres I really like this approach. As mentioned, it keeps the token on the server and means I have a central location to retrieve the token from my various server components.

The issue I have however is although this works locally, I retrieve an error when running in Production (which is on Vercel): Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers

It seems that passing the NextJS headers() to the PATCH route handler seems to throw this error 🙄 I can't see anything that is actually modifying the headers, but unfortunately don't have any other ideas on this other than storing the token on the client side which I was hoping to avoid.

oskarssylwan commented 8 months ago

I tried to get the token from cookies() in my server components and log them. 2 tokens get logged: next-auth.session-token.0 next-auth.session-token.1

I must've been doing something wrong in the callbacks to store 2 of them. However, both signatures are invalid according to jwt.io when i copy them there.

I also got 2 session tokens and was confused by it. It looks like NextAuth is chunking the session cookie it it is too large. You can see it if you turn on debug mode

oskarssylwan commented 8 months ago

So to summarise we have 2 options?

  1. Expose the access_token in the session which is unfavourable since it will expose it to the client
  2. Make calls to a route handler inside the components to get it, which is seen as an anti pattern by Vercel because of the extra network calls.

Is there really no better way to get it?

starlight-akouri commented 7 months ago

It seems like the new universal auth() method in v5 is supposed to handle this, but it does not look like it's implemented yet? Can someone confirm?

impact-ls commented 7 months ago

In the new docs it says that all should be replaced with auth(), but as of now it only returns a session.

Maybe a stupid question, but why is exposing accessToken to the client bad? Isn't it usually stored in the cookies and is encrypted anyway?

It seems adding a Route Handler just for getting the accessToken via getToken is not necessary if we can use getToken on the server. This way we get rid of unnecessary network calls.

I am still waiting for the new docs to give an example or explain how to do it properly in v5. I have two problems:

  1. I have trouble refreshing the token. I followed the docs but I don't know how to update the token with the new one. getToken from next-auth/jwt does not update after jwt callback returns the new token.

  2. I would like to automatically signOut when the refresh token expires. Is it possible from the server (inside the callback)? It worked for me except that there is an error thrown that the cookies are modified not in the Server Action or a Route Handler, so I assume it was not intended to be done this way.

teapot2 commented 6 months ago

next-auth is awful lol

alarv commented 4 months ago

It's 2024 and the most popular JS framework and the most popular auth library to go with it does not return the access token, I thought this is trivial.

Yazan-Ali-01 commented 4 months ago

I believe the problem got fixed in the newer next auth version with the gloabl auth()

joostfarla commented 3 months ago

I faced the same issue, and started a thread on Discord: https://discord.com/channels/1200116961590399008/1257260179683283048

It might help if others provide additional input there.

gaganbiswas commented 3 months ago

On nextjs 14, I got the access_token in the rsc, actions and routes by creating a cookie in the jwt() function using cookies() from next/headers. Doing this ensured that my access_token can't be accessed from the client or by going to /api/auth/session. I also handled some situations like deleting my cookies on expiry syncing its expiry with my session cookies from next-auth.

async jwt({ token, user }): Promise<JWT | null> {
      const cookie = cookies();
      if (user) {
        const accessDecode = jwtDecode(user.accessToken);

        const userProfile: User = {
          id: token.sub,
          name: user.name,
          email: user.email,
          image: user.image,
          accessToken: "",
          refreshToken: "",
          username: user.username,
        };

        cookie.set("access-token", user.accessToken, {
          expires: accessDecode.exp! * 1000,
          httpOnly: true,
          secure: process.env.NODE_ENV === "production",
          sameSite: "lax",
        });

        return {
          access_token: user.accessToken,
          expires_at: accessDecode.exp as number,
          refresh_token: user.refreshToken,
          user: userProfile,
        };
      } else if (Date.now() < token.expires_at * 1000) {
        return token;
      } else {
        if (!token.refresh_token) {
          cookie.delete("access-token");
          throw new Error("Missing refresh token");
        }

        try {
          const { data } = await axios.get(
            process.env.API_URL + "/user/generate-new-refresh-token",
            {
              headers: {
                Authorization: token.access_token as string,
                refreshToken: token.refresh_token as string,
              },
            }
          );

          if (data.error) throw new Error("Bad request");

          const accessDecode = jwtDecode(data.data.accessToken);

          cookie.set("access-token", data.data.accessToken, {
            expires: accessDecode.exp! * 1000,
            httpOnly: true,
            secure: process.env.NODE_ENV === "production",
            sameSite: "lax",
          });

          return {
            // Keep the previous token properties
            ...token,
            access_token: data.data.accessToken,
            expires_at: accessDecode.exp as number,
            refresh_token: data.data.refreshToken,
          };
        } catch (error) {
          cookie.delete("access-token");
          return null;
        }
      }
    },
    async session({ session, token }) {
      if (token.user) {
        // @ts-ignore
        session.user = token.user as User;
      }

      return session;
    },
  },
  session: {
    maxAge: 60 * 60,
  },

I guess this is quite a messy approach but atleast for now it insures that my access_token is safe in the cookies and is not exposed to the client. Hope, next-auth fixes this soon. :D

anaclumos commented 3 months ago

You can use this in Next.js App Router!

import { Suspense } from "react"
import { getServerSession } from "next-auth/next"
import { Session } from "@/types/session"
import { authOptions } from "@/lib/auth"
import { cookies } from 'next/headers';

export default async function JobsPage() {
  const session: Session | null = await getServerSession(authOptions)

  if (!session) {
    redirect("/profile") // should not happen due to the middleware
  }

+  const cookieStore = cookies();
+  const sessionToken = cookieStore.get('next-auth.session-token');

...
aayushgelal commented 3 months ago

I made it working by passing nextAuthOptions as parameter in getServerSession function. This should work for you as well. const session = await getServerSession(nextAuthOptions); // 👈 added this where nextAuthOptions = ({ providers: ....

});

alinasiri8102 commented 2 months ago

i'm using this approach in one of my server Actions and it works fine: https://github.com/nextauthjs/next-auth/pull/5792#issue-1444863063

import { headers, cookies } from "next/headers";
import { getToken } from "next-auth/jwt";

 const token = await getToken({
    req: {
      headers: Object.fromEntries(headers()),
      cookies: Object.fromEntries(
        cookies()
          .getAll()
          .map((c) => [c.name, c.value])
      ),
    },
  });

  console.log(token);
arohman84 commented 2 months ago
cookie.delete("access-token");
          return null;

unfortunately I got error for next 14.2.5 and next-auth 5.0.0-beta.20 when using cookie from next/headers, here's the server log:

Error: Cookies can only be modified in a Server Action or Route Handler.

so weird the console.log visible on server log but auth said this is not server action .... hmmmmm

gaganbiswas commented 2 months ago

Error: Cookies can only be modified in a Server Action or Route Handler.

Well you can create a deleteCookie action in action.ts and then call the deleteCookie fn from the catch block.

I actually dropped the idea of using next-auth cz i was getting into lot of problems with the beta version and i went ahead and used my own auth management using providers and interceptors.

dion-blutui commented 2 months ago

@avillegastorres Any idea why I get this error on calling getToken() inside a API route.

  const token = await getToken({
    req,
    secret
  })

Error Argument of type '{ req: NextRequest; secret: string; }' is not assignable to parameter of type 'GetTokenParams<false>'. Property 'salt' is missing in type '{ req: NextRequest; secret: string; }' but required in type 'GetTokenParams<false>'.

auth.ts


    jwt({ token, profile }) {
      if (profile) {
        token.id = profile.id
        token.image = profile.avatar_url || profile.picture
        token.accessToken = profile.accessToken
      }
      return token
    },
    session: ({ session, token }) => {
      if (session?.user && token?.id) {
        session.user.id = String(token.id)
      }

      return session
    },
    authorized({ auth }) {
      return !!auth?.user // this ensures there is a logged in user for -every- request
    }
  },```
diego-lipinski-de-castro commented 1 month ago

Bruv....its crazy we cant easily get the acesstoken after signing in, this package is basically useless

UpTEN commented 1 month ago

I'm really shocked how complicated it is, only to get the access token to send it to my backend api in requests. Seems that Auth.js just isn't the right tool for that simple task.

joaku commented 1 month ago

Solution here. Hope it helps!

import { headers, cookies } from 'next/headers' import { getToken } from 'next-auth/jwt' import jwt from 'jsonwebtoken'

// Obtener headers y cookies const reqHeaders = Object.fromEntries(headers()) const reqCookies = Object.fromEntries( cookies() .getAll() .map((c) => [c.name, c.value]) )

// Obtener el token usando getToken const token = await getToken({ req: { headers: reqHeaders, cookies: reqCookies } as any })

if (token == null) { throw new Error('Token not found') }

// Firmar el token usando la clave secreta const secret = process.env.NEXTAUTH_SECRET ?? 'mysecretkey' const accessToken = jwt.sign(token, secret)

rsalcedo24 commented 1 week ago

All I do was to pass authOptions to await getServerSession(authOptions);

If in you session callback your are passing accesstoken from the token to the session then you can just pass authOptions to getServerSession(authOption) and you will get the user object and the accessToken.

If this is what you got:

async session({ session, token }: { session: any; token: JWT }) {
      session.accessToken = token.accessToken;
      session.user.name = token.name;
      session.user.email = token.email;

      return session;
},

Then just pass:

   await getServerSession(authOptions);
N-Argyle commented 2 days ago

Here's a working method as of October. Previous methods did not work because getToken no longer supports cookies:

import { decode } from 'next-auth/jwt'
import { cookies } from "next/headers";

// --- in your function
const sessionTokenName = 'authjs.session-token'; // or 'next-auth.session-token' if using next-auth
const sessionToken = cookies().getAll().find(c => c.name === sessionTokenName);
const token = await decode({
  token: sessionToken?.value,
  secret: env.NEXTAUTH_SECRET!,
  salt: sessionTokenName,
});