How to add backend JWT and data to NextAuth session?
Answered
Baldfaced hornet posted this in #help-forum
Baldfaced hornetOP
I am using credentials for nextauth and am able to get my user info from the backend api but having trouble on accessing it via nextauth session.
The backend returns:
The commented out lines below my console.log(user) is where most of the confusion comes in... I saw I may need to change what is held by the session itself in the docs?
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import postUserLogin from "@/lib/postUserLogin";
import {pages} from "next/dist/build/webpack/loaders/next-route-loader/templates/app-page";
const handler = NextAuth({
pages: {
},
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: {
label: "Email:",
type: "text",
placeholder: "example@gmail.com"
},
password: {
label: "Password:",
type: "password"
}
},
async authorize(credentials: { email?: string; password?: string}){
// This is where we will post the email and password to Hades... if login is succesful hades will return info and a JWT to use in session
// Docs: https://next-auth.js.org/configuration/providers/credentials
const user = await postUserLogin(credentials.email, credentials.password)
// TODO: LOGIN SUCCESSFUL, need to add response to session or jwt(objects returned are persisted to the JWT)
console.log(user)
if (user) {
return user
} else {
return null
}
}
})
],
// session: {
// strategy: "jwt",
// },
// secret: process.env.NEXTAUTH_SECRET,
// debug: process.env.NODE_ENV === "development",
// callbacks: {
// // async jwt ({ token, user, session}) {
// // return token;
// // },
// async session ({ session, token, user}) {
// return session;
// }
// },
theme: {
colorScheme: "auto", // "auto" | "dark" | "light"
// brandColor: "#38bdf8", // Hex color code
// logo: "", // Absolute URL to image
// buttonText: "#f59e0b" // Hex color code
}
})
export { handler as GET, handler as POST }The backend returns:
{token: "redacted", userName: "charris", identityId: "1", userId: "1", userRole: "USER"}The commented out lines below my console.log(user) is where most of the confusion comes in... I saw I may need to change what is held by the session itself in the docs?
Answered by Baldfaced hornet
Side note I was able to get the info into the token and then the token into the session. It is working now mostly as intended.... However I am still getting the TypeScript complaints and will be working on editing the user/token/session types to accommodate the attributes I need to pass around
45 Replies
Baldfaced hornetOP
Bump
Toyger
you have session callback
iirc your returned data should be either in
async session({ session, token, user }) {
session.accessToken = token.accessToken
session.user.id = token.id
return session
}iirc your returned data should be either in
token or in user and you can save it to session here.Baldfaced hornetOP
So I dug a lot further into the docs and basically it says to add the user.token to the token.accessToken during the jwt callback
then for the session callback you do as you did above @Toyger
Issue I am having...
then for the session callback you do as you did above @Toyger
Issue I am having...
It hates me trying to use token.accessToken and user.token like what is shown in the next-auth docs for JWT callbacks
@Baldfaced hornet It hates me trying to use token.accessToken and user.token like what is shown in the next-auth docs for JWT callbacks
Toyger
I thought you want to add your backend jwt as additional field that you want to handle somehow later, because right now you are trying to replace next-auth jwt which will break authentication.
@Toyger I thought you want to add your backend jwt as additional field that you want to handle somehow later, because right now you are trying to replace next-auth jwt which will break authentication.
Baldfaced hornetOP
My understanding which could be flawed:
I have JWT in my backend used for AUTH. When a user signs in they provide their email/pw which I then send back an objectfrom the backend with the jwt, username, userId etc(non personal info). This all is captured by next-auth and temporary stored as "user".
I then need to transfer that information from user onto the token (then from the token to the session) so it is accessible and have the backend JWT stored to send back for other calls to the backend which require being logged in to use.
I have JWT in my backend used for AUTH. When a user signs in they provide their email/pw which I then send back an objectfrom the backend with the jwt, username, userId etc(non personal info). This all is captured by next-auth and temporary stored as "user".
I then need to transfer that information from user onto the token (then from the token to the session) so it is accessible and have the backend JWT stored to send back for other calls to the backend which require being logged in to use.
@Baldfaced hornet My understanding which could be flawed:
I have JWT in my backend used for AUTH. When a user signs in they provide their email/pw which I then send back an objectfrom the backend with the jwt, username, userId etc(non personal info). This all is captured by next-auth and temporary stored as "user".
I then need to transfer that information from user onto the token (then from the token to the session) so it is accessible and have the backend JWT stored to send back for other calls to the backend which require being logged in to use.
Toyger
yeah so you need to put it into session on some different key, if your backend token in
then in session callback something like
then you can use it in client with
user.tokenthen in session callback something like
async session({ session, user, token }) {
if (session.backend_jwt===undefined){
session.backend_jwt=user.token
}
return session
},then you can use it in client with
useSession hook, or on server with getServerSessionBaldfaced hornetOP
So right now if I try to log user in the session callback nothing happens
I believe I have to first set the user to the token due to the docs
If I do the JWT callback first I do have a "user" that exist
---------------------------------------------------------------------------------------------
So I can see the objects are there and do exist but say if I try to access them I get red squigly test... I am assuming due to TypeScript and having to extend the token and user models for next-auth
Toyger
it strange, because it should populate session with
user object like session.userBaldfaced hornetOP
I think it is forcing the use of JWT because I am not using an Adaptor to connect straight to a database and instead opting to go to an api endpoint.
So because JWT is being forced it is making me jump through these loop holes to add it back to the session which is kind of dumb.... Have to run out but will continue working on it and post any progress later
So because JWT is being forced it is making me jump through these loop holes to add it back to the session which is kind of dumb.... Have to run out but will continue working on it and post any progress later
@Baldfaced hornet I think it is forcing the use of JWT because I am not using an Adaptor to connect straight to a database and instead opting to go to an api endpoint.
So because JWT is being forced it is making me jump through these loop holes to add it back to the session which is kind of dumb.... Have to run out but will continue working on it and post any progress later
Toyger
you need to try with clean nextjs project, and basic endpoint, I configured basic example couple days ago, and it works fine with Credentials provider and simple API that just returned json with user info.
@Baldfaced hornet Are you using TypeScript?
Toyger
tried with javascript, but with typescript for testing purposes you can cast errors of type with
as any to check if it works, and later fix types definitionsBaldfaced hornetOP
Ran a sanity check and it is just a typescript/ide throwing "Unresolved variable" issue
So just need to figure out how to adjust next-auth to work with TypeScript and should be okay I think.
Although my first attempt to do this didn't work out haha
Although my first attempt to do this didn't work out haha
------------------------------------------------------------------------
Soo will play around with these 3 things when I get back later to see what needs changed(next-auth type file location, the file itself, and the typescript config file)
Soo will play around with these 3 things when I get back later to see what needs changed(next-auth type file location, the file itself, and the typescript config file)
Almond stone wasp
following! Ironically I am in a similar boat... I need my jwt from useSession aswell Here is where I am at:
Almond stone wasp
OH I think I just got it thanks to @Toyger .. check out https://nextjs.org/docs/app/api-reference/functions/cookies
import { cookies } from 'next/headers';
@Almond stone wasp OH I think I just got it thanks to <@536484914221285376> .. check out https://nextjs.org/docs/app/api-reference/functions/cookies
Baldfaced hornetOP
Kind of a weird work around... Next-Auth handles the cookies for you and you shouldn't need to use the next cookies functions to make it work... I would re-consider how this is working.
Baldfaced hornetOP
Side note I was able to get the info into the token and then the token into the session. It is working now mostly as intended.... However I am still getting the TypeScript complaints and will be working on editing the user/token/session types to accommodate the attributes I need to pass around
Answer
Baldfaced hornetOP
@Baldfaced hornet Side note I was able to get the info into the token and then the token into the session. It is working now mostly as intended.... However I am still getting the TypeScript complaints and will be working on editing the user/token/session types to accommodate the attributes I need to pass around
Almond stone wasp
Yeah, I am looking for an alternative. I was able to retrieve the jwt that was in Cookie but it actually wasn't the correct auth cookie that my API needs. As far as your TS error, im using JS so I am no help there.
@Almond stone wasp Yeah, I am looking for an alternative. I was able to retrieve the jwt that was in Cookie but it actually wasn't the correct auth cookie that my API needs. As far as your TS error, im using JS so I am no help there.
Baldfaced hornetOP
const handler = NextAuth({
pages: {
},
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: {
label: "Email:",
type: "text",
placeholder: "example@gmail.com"
},
password: {
label: "Password:",
type: "password"
}
},
async authorize(credentials: { email?: string; password?: string}){
// This is where we will post the email and password to Hades... if login is succesful hades will return info and a JWT to use in session
// Docs: https://next-auth.js.org/configuration/providers/credentials
const user = await postUserLogin(credentials.email, credentials.password)
// TODO: LOGIN SUCCESSFUL, need to add response to session or jwt(objects returned are persisted to the JWT)
console.log(user)
if (user) {
return user
} else {
return null
}
}
})
],
session: {
strategy: "jwt",
},
// secret: process.env.NEXTAUTH_SECRET,
// debug: process.env.NODE_ENV === "development",
callbacks: {
async jwt ({ token, user, session}) {
if(user){
token.backendToken = user.token;
token.userName = user.userName;
token.identityId = user.identityId;
token.userId = user.userId;
token.userRole = user.userRole;
console.log(user)
console.log('b')
console.log(token)
}
return token;
},
async session ({ session, token, user}) {
if(token){
session.backendToken = token.backendToken
session.user.userName = token.userName;
session.user.identityId = token.identityId;
session.user.userId = token.userId;
session.user.userRole = token.userRole;
console.log('TEST' + session.user.userName)
}
return session;
}
},
theme: {
colorScheme: "auto", // "auto" | "dark" | "light"
// brandColor: "#38bdf8", // Hex color code
// logo: "", // Absolute URL to image
// buttonText: "#f59e0b" // Hex color code
}
})Here's mine that is working @Almond stone wasp to maybe help ya along
@Almond stone wasp Yeah, I am looking for an alternative. I was able to retrieve the jwt that was in Cookie but it actually wasn't the correct auth cookie that my API needs. As far as your TS error, im using JS so I am no help there.
Baldfaced hornetOP
-----------------------------------------------------------
In the code you provided above I don't see where you're reaching out to sign in... but If you are using email/pw sign in (CredentialsProvider) then that will be stored as "user" in the jwt callback.
So the response from your backend should be able to be retrieved inside the "async jwt" part of "callbacks"
In the code you provided above I don't see where you're reaching out to sign in... but If you are using email/pw sign in (CredentialsProvider) then that will be stored as "user" in the jwt callback.
So the response from your backend should be able to be retrieved inside the "async jwt" part of "callbacks"
I would set up a if(user) { console.log(user) } so you can verify inside of your JWT callback
As long as that exist then you are good to set a token.whatEverYouWantToNameItHere = user.backendToken then return token; inside of your async jwt callback
then inside your async session callback you'll want to set a session.whatEverYouWantToNameItHere = token.whatEverYouWantToNameItHere and then return session
Baldfaced hornetOP
I'll keep this topic open for now in case you have some more questions for me @Almond stone wasp but going to close it out soon and open a new one relating to the typescript issues and mark this as resolved
Almond stone wasp
Thank you @Baldfaced hornet . Yeah I am still stuck on getting access to my jwt that comes back from my login API. From my login API the data is returned like: { jwt: 'xzy...', user: { username: 'abc', email: 'abc@email.com' ....} }. So in await res.json() I am deconstructing the user and jwt. I just don't know what to do with the jwt from there. I need access to it. I do not see it in the jwt or session callbacks.
Here is my authorize: async authorize(credentials, req) {
const baseURL = process.env.NEXT_PUBLIC_STRAPI_URL;
if (credentials == null) return null;
if (!credentials.email || !credentials.password) {
return null;
}
//console.log(credentials);
try {
const res = await fetch(
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
identifier: credentials.email,
password: credentials.password,
}),
});
const { user, jwt } = await res.json();
console.log(' next-auth user', user);
console.log(' next-auth jwt', jwt);
// If no error and we have user data, return it
if (res.ok && user) {
return {
...user,
name:
};
}
// Return null if user data could not be retrieved
return null;
} catch (error) {
// Sign In Fail
return null;
}
},
const baseURL = process.env.NEXT_PUBLIC_STRAPI_URL;
if (credentials == null) return null;
if (!credentials.email || !credentials.password) {
return null;
}
//console.log(credentials);
try {
const res = await fetch(
${baseURL}/api/auth/local, {method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
identifier: credentials.email,
password: credentials.password,
}),
});
const { user, jwt } = await res.json();
console.log(' next-auth user', user);
console.log(' next-auth jwt', jwt);
// If no error and we have user data, return it
if (res.ok && user) {
return {
...user,
name:
${user.firstName} ${user.lastName},};
}
// Return null if user data could not be retrieved
return null;
} catch (error) {
// Sign In Fail
return null;
}
},
@Almond stone wasp Here is my authorize: async authorize(credentials, req) {
const baseURL = process.env.NEXT_PUBLIC_STRAPI_URL;
if (credentials == null) return null;
if (!credentials.email || !credentials.password) {
return null;
}
//console.log(credentials);
try {
const res = await fetch(`${baseURL}/api/auth/local`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
identifier: credentials.email,
password: credentials.password,
}),
});
const { user, jwt } = await res.json();
console.log('** next-auth user', user);
console.log('** next-auth jwt', jwt);
// If no error and we have user data, return it
if (res.ok && user) {
return {
...user,
name: `${user.firstName} ${user.lastName}`,
};
}
// Return null if user data could not be retrieved
return null;
} catch (error) {
// Sign In Fail
return null;
}
},
Baldfaced hornetOP
After you get the res just check if res is there and okay then return response.
Then in your async jwt callback try to deconstruct "user" as if it was your "res" from above
Then in your async jwt callback try to deconstruct "user" as if it was your "res" from above
@Almond stone wasp Here is my authorize: async authorize(credentials, req) {
const baseURL = process.env.NEXT_PUBLIC_STRAPI_URL;
if (credentials == null) return null;
if (!credentials.email || !credentials.password) {
return null;
}
//console.log(credentials);
try {
const res = await fetch(`${baseURL}/api/auth/local`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
identifier: credentials.email,
password: credentials.password,
}),
});
const { user, jwt } = await res.json();
console.log('** next-auth user', user);
console.log('** next-auth jwt', jwt);
// If no error and we have user data, return it
if (res.ok && user) {
return {
...user,
name: `${user.firstName} ${user.lastName}`,
};
}
// Return null if user data could not be retrieved
return null;
} catch (error) {
// Sign In Fail
return null;
}
},
Baldfaced hornetOP
const baseURL = process.env.NEXT_PUBLIC_STRAPI_URL;
if (credentials == null) return null;
if (!credentials.email || !credentials.password) {
return null;
}
//console.log(credentials);
try {
const res = await fetch(${baseURL}/api/auth/local, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
identifier: credentials.email,
password: credentials.password,
}),
});
// If no error and we have user data, return it
if (res.ok && user) {
return res
}
// Return null if user data could not be retrieved
return null;
} catch (error) {
// Sign In Fail
return null;
}
}, ^Edited what you had to show what I mean
So when you return "res" it will have all the info on the "user" object that exist inside of the async JWT callback
@Baldfaced hornet So when you return "res" it will have all the info on the "user" object that exist inside of the async JWT callback
Almond stone wasp
Thank you! I ended up getting there. I assinged my token in the jwt and session callback. Then to access my token serverside I used: import { getServerSession } from 'next-auth/next'; import { authOptions } from '../api/auth/[...nextauth]/route'; and then const session = await getServerSession(authOptions);