NextAuth Problem
Answered
Saltwater Crocodile posted this in #help-forum
Saltwater CrocodileOP
So i currently have a Problem with nextAuth. I have a NExt JS Frontend with NestJS Backend. When logging in the Backend sends BackendTokens back to the user. I set the Token and Session Structure int next-auth.d.ts like this:
Now when logging in with Credentials this JWT Structure get applied with all the necessary details.
But when logging in with Google Provider the normal structre is applied and this one is ignored. Does anyone know where i can apply this to the Providers?
import NextAuth from "next-auth/next";
declare module "next-auth" {
interface Session {
user: {
id: string;
email: string;
name: string
role: string
provider: string
};
backendTokens: {
accessToken: string
refreshToken: string
expiresIn: number
}
}
}
import { JWT } from "next-auth/jwt";
declare module "next-auth/jwt" {
interface JWT {
user: {
id: string;
email: string;
name: string
role: string
provider: string
};
backendTokens: {
accessToken: string
refreshToken: string
expiresIn: number
}
}
}Now when logging in with Credentials this JWT Structure get applied with all the necessary details.
But when logging in with Google Provider the normal structre is applied and this one is ignored. Does anyone know where i can apply this to the Providers?
Answered by American black bear
In my case for my app i need a token that backend provides, so when user sign in with Google, how i Say intercept that in jwt and consume a endopoint that provides me a token only in this specific scenarios and then return that
93 Replies
Saltwater CrocodileOP
@Wesley Janse
GoogleProvider({
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
// profile(profile) {
// console.log("Profile: ", profile)
// return { role: profile.role ?? "user", ...profile }
// },
}),I tried to do it with the profile but it doesnt work
why are you doing things in the next-auth.d.ts?
I am not 100% sure, but this might just work too if you create a custom provider and adapting the callbacks to make sure your structure is the way you want it to be:
https://next-auth.js.org/configuration/callbacks
I am not 100% sure, but this might just work too if you create a custom provider and adapting the callbacks to make sure your structure is the way you want it to be:
https://next-auth.js.org/configuration/callbacks
Saltwater CrocodileOP
How would that look? I always did it like this, but now is the first time im using a Provider
When looking videos they always did it in the next-auth.d.ts
I've never worked with an Adapter
American black bear
you can intercept the login if the user logged in with google and convert the data that google provide you into the structure that you want
you can intercept that in the callbacks, i recomend you to intercep in jwt callback
Californian
Good answers
Saltwater CrocodileOP
hm okay
Can you give me an example how that would look in the jwt callback?
@American black bear
And where i can set the structure there
Saltwater CrocodileOP
i currently do it like this
async jwt({ token, user }) {
return { ...token, ...user }
// if (new Date().getTime() < token.backendTokens.expiresIn) return token
// return await refreshToken(token)
},
async session({ token, session, user }) {
session.user = token.user
session.backendTokens = token.backendTokens
return session
}American black bear
/
Saltwater CrocodileOP
@American black bear
Have you looked at this piece of documentation? https://next-auth.js.org/v3/tutorials/refresh-token-rotation
Saltwater CrocodileOP
I think the problem is, when logging in with Google the Token is Undefined, but with credentials i get the information
When logging in with Google i use the signIn Callback for the Database logic like this
async signIn({ user, account }) {
console.log("SIGNIN")
const name = user.name
const email = user.email
const provider = account?.provider.toUpperCase()
const res = await fetch(BACKEND_URL + "/auth/provider", {
method: "POST",
body: JSON.stringify({
name, email, provider
}),
headers: {
"Content-Type": "application/json"
}
})
return true
},And because i can only return true here
I think the token is undefined
Does that make sense?
But i dont know any other way to do the database logic on the google provider
Wuchang bream
what strategy you are using
database or jwt ?Saltwater CrocodileOP
jwt
Wuchang bream
did you write your own
encode and decode functions for jwt ?Saltwater CrocodileOP
no i didn't
I mean next auth does that for you no?
Wuchang bream
yes , it does .but once check if you are receiving JWT
Saltwater CrocodileOP
When console logging jwt on credentials i can see all the right values
Do you mean that?
Wuchang bream
yes
Saltwater CrocodileOP
Yes taht works
Only on Provider that doesnt work
Since the signIn function only returns true i guess
Can you give me any other logic how i can fix that?
Wuchang bream
I just followed the NextAuth docs and things worked for me
Saltwater CrocodileOP
With Google Provider?
Could you set custom payload?
Wuchang bream
I did it with Github Provider
Saltwater CrocodileOP
What payload did you get?
Wuchang bream
what do you mean by payload ?
I received JWT and user info
like email , id etc...
Saltwater CrocodileOP
yes but i need the payload to be like this:
user: {
id: string;
email: string;
name: string
role: string
provider: string
};
backendTokens: {
accessToken: string
refreshToken: string
expiresIn: number
}Wuchang bream
I didn't do custom payload with NextAuth
Saltwater CrocodileOP
never?
Wuchang bream
but I know we can add custom properties
I use supabase for auth
Saltwater CrocodileOP
Yes with Credentials it worked but not wtih Google Provider
@American black bear
Maybe you know a way?
Saltwater CrocodileOP
I feel like the thing i need to find out is how to Sign in wth Google Provider and then return the response from the server from this signin and not only true
Because the server response is the data i need to store in the session
Saltwater CrocodileOP
Okay that's weird
Now i got it to return the server respnse on the signIN Callback without an erro but the jwt payload still stays the default
async signIn({ user, account }) {
if (account?.provider === "credentials") return true
const name = user.name
const email = user.email
const provider = account?.provider.toUpperCase()
const res = await fetch(BACKEND_URL + "/auth/provider", {
method: "POST",
body: JSON.stringify({
name, email, provider
}),
headers: {
"Content-Type": "application/json"
}
})
const response = await res.json()
console.log("RESPONSE: ", response)
return response
},But i checked when doing it wiht the credentials the response is exactly the same
But on the jwt callback it doesnt apply
American black bear
here is what a did
I'm looking at this and trying to emulate right now. Hopefully can help soon as well. Side question- once you have the access token, how are you retriving it in server components? getServerSession is giving me an handlers {} object 😕
Saltwater CrocodileOP
@American black bear but now i still get the normal payload but extended with the profile values of Google Provider
What i need is a payload only with my values
American black bear
Yeah, when You have the data from Google, you can convert that into a regular payload, you can even use an endopoint to get the same response that You get on sign in with credencials
Saltwater CrocodileOP
Is it even possible to do a custom payload on Google Provider?
@American black bear Yeah, when You have the data from Google, you can convert that into a regular payload, you can even use an endopoint to get the same response that You get on sign in with credencials
Saltwater CrocodileOP
But can i add values to the google payload?
American black bear
No, but in jwt callback You Will always return the sesion, so you can intercept when the users sign in with other providers that is not credentials and extract the data that Google provides You in the props, and return a payload like You want
Saltwater CrocodileOP
But only with values that google provides?
Or can i add custom values
Because i need a role value
And the backendrokens
American black bear
Inside of this interceptor You can call a endopoint that return whatever you want, and return the sesion as you want, but remeber that Google does not return a password
Saltwater CrocodileOP
Ohh okay
So inside the jwt i make another api call and then i assign the values again there
American black bear
Yes, but does miss the account conditional
American black bear
In my case for my app i need a token that backend provides, so when user sign in with Google, how i Say intercept that in jwt and consume a endopoint that provides me a token only in this specific scenarios and then return that
Answer
Saltwater CrocodileOP
@American black bear Thank you so much
Now it works
It looks like this now:
async jwt({ token, user, account }) {
if (account && ["google"].includes(account.provider)) {
const name = user.name
const email = user.email
const provider = account?.provider.toUpperCase()
const res = await fetch(BACKEND_URL + "/auth/provider", {
method: "POST",
body: JSON.stringify({
name, email, provider
}),
headers: {
"Content-Type": "application/json"
}
})
const response = await res.json()
token.user = response.user
token.backendTokens = response.backendTokens
}
return { ...token, ...user }
// if (new Date().getTime() < token.backendTokens.expiresIn) return token
// return await refreshToken(token)
},
async session({ token, session, user }) {
session.user = token.user
session.backendTokens = token.backendTokens
return session
}But another question. The jwt callbacks gets called on all authentication steps right? Is it bad practice then to do it like this, because now every authentication step the endpoint gets called?
Or is that not an issue
American black bear
With account validation the code that You write for signin with Google will only ecxecute when the user do a sign in, if the session is requested the jwt Will be ecxecute but the account only exist in sign in so with that validation we Will be able to use the code to a regular session and how in the sign in we already conver the session as we want, there won't by any problem. You can check that by putting a console.log in the jwt and check how it works
Saltwater CrocodileOP
Oh okay
I get it
Thank you very muhc
Finally i could solve this Problem
<3
American black bear
