I'm using NextJs with Auth.Js version 5, How can I retrive the access_token upon sign in so as to make further API calls

78 views Asked by At

Upon successfull sign in using custom credentials provider in the NextAuth.Js v5, obtaining the access_token to make further API calls is unsuccessful. Only a subset of the token is returned, apparently for increased security. But without the access_token, how am I supposed to make API calls which need this in the header? I am using version 5 not 4

The v5 documentation states to use callbacks to store the access_token in a session. This is auth.ts, the console.logs do not output the access token in the callbacks.

I used this resource as further instructions for v5 text

import type { NextAuthConfig } from 'next-auth';
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';

const credentialsConfig = CredentialsProvider({
  name: 'Credentials',
  credentials: {
    username: {
      label: 'Username or Account Number',
    },
    password: {
      label: 'password',
      type: 'password',
    },
  },
  async authorize(credentials) {
    const postdata = {
      userName: credentials.username,
      password: credentials.password,
    };
    let base64encodedData = process.env.LOGIN_BASE64;

    const authResponse = await fetch(
      'https://my-login-endpoint.com',
      {
        method: 'POST',
        headers: {
          Authorization: `Basic ${base64encodedData}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(postdata),
      },
    );

    if (!authResponse.ok) {
      return null;
    }

    const user = await authResponse.json();

    if (user) {
      console.log('USER', user); // access_token is displayed here ok!
      return user;
    }

    return null;
  },
});

const config = {
  providers: [credentialsConfig],
  trustHost: true,
  debug: process.env.NODE_ENV !== 'production',
  session: {
    strategy: 'jwt',
  },
  async session({ session, user, token }) {
      if (token.access_token) {
        session.access_token = token.access_token; // Put the provider's access token in the session so that we can access it client-side and server-side with `auth()`
      }
      console.log(`Auth Sess = ${JSON.stringify(session)}`); // Auth Sess = {"user":{},"expires":"2024-04-12T09:27:58.960Z"}
      console.log(`User = ${JSON.stringify(user)}`); // Undefined
      console.log(`Auth Tok = ${JSON.stringify(token)}`); // {"sub":"12345678-1234-4567-1234-123456789111","iat":yyyyyyyyyy,"exp":1712914043,"jti":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}

      return session;
    },
    async jwt({ token, user, account, profile, isNewUser }) {
      console.log(`Auth JWT Tok = ${JSON.stringify(token)}`); // {"sub":"12345678-1234-4567-1234-123456789111","iat":yyyyyyyyyy,"exp":1712914043,"jti":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}
      console.log(`USER JWT account = ${JSON.stringify(user)}`); // UNDEFINED
      console.log(`Router Auth JWT account = ${JSON.stringify(account)}`); // UNDEFINED

      if (account) {
        token.accessToken = account.access_token;
      }

      return token;
    },

    authorized({ auth, request: { nextUrl } }) {
      const { pathname } = nextUrl;
      if (pathname === '/middlewareProtected') return !!auth; // return by converting the auth session to a boolean
      return true;
    },
  },
} satisfies NextAuthConfig;

export const { handlers, auth, signIn, signOut } = NextAuth(config);

This a server page

import { H3, UnauthorisedMessage } from '@repo/ui';
import { auth } from 'auth';
import React from 'react';

const Page = async () => {
  const session = await auth();

  if (!session || !session.user) {
    return <UnauthorisedMessage />;
  }
  return (
    <>
      <H3>Server page must be protected</H3>
      <p>{`${JSON.stringify(session)}`}</p>
    </>
  );
};

export default Page;
2

There are 2 answers

0
kzi On

You can use getServerSession at server pages:

import { getServerSession } from 'next-auth';
import { H3, UnauthorisedMessage } from '@repo/ui';
import { authOptions } from './path/to/your/authOptions';

const Page = async () => {
  const session = await getServerSession(authOptions);

  if (!session || !session.user) {
    return <UnauthorisedMessage />;
  }
  return (
    <>
      <H3>Server page must be protected</H3>
      <p>{`${JSON.stringify(session)}`}</p>
    </>
  );
};

export default Page;

Obviously you have to replace ./path/to/your/authOptions with the path to your authOptions. Have a look here for further documentation.

0
Kris Kristiansen On

Because I am using v5 with the Credentials Provider, what eventually worked for me was to add access_token to the typescript User interface:

import NextAuth, { DefaultSession } from 'next-auth';
import { DefaultJWT } from '@auth/core/jwt';

declare module 'next-auth' {

  // Extend user to reveal access_token
  interface User {
    access_token: string | null;
  }

  // Extend session to hold the access_token
  interface Session {
    access_token: (string & DefaultSession) | any;
  }

  // Extend token to hold the access_token before it gets put into session
  interface JWT {
    access_token: string & DefaultJWT;
  }
}