How to use Next Auth & OAuth to fetch from a Google API
Answered
Oriental Scops-Owl posted this in #help-forum
Oriental Scops-OwlOP
I'm using NextAuth's Google Provider for Google account sign-ins. I've successfully authenticated users and now I'd like to make API calls to YouTube's API for authenticated user details. I am not sure how to use the access_token or id_token from Google in my GET requests.
Here's an example of what my logs show in terms of the response I get back from Google when I sign in (personal details redacted for privacy):
Here's an example of what my logs show in terms of the response I get back from Google when I sign in (personal details redacted for privacy):
[next-auth][debug][OAUTH_CALLBACK_RESPONSE] {
profile: {
id: '[REDACTED]',
name: '[REDACTED]',
email: '[REDACTED]',
image: '[REDACTED]'
},
account: {
provider: 'google',
type: 'oauth',
providerAccountId: '[REDACTED]',
access_token: '[REDACTED]',
expires_at: [REDACTED],
scope: 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/youtube.readonly openid https://www.googleapis.com/auth/userinfo.email',
token_type: 'Bearer',
id_token: '[REDACTED]',
},
OAuthProfile: {
iss: 'https://accounts.google.com',
azp: '[REDACTED]',
aud: '[REDACTED]',
sub: '[REDACTED]',
email: '[REDACTED]',
email_verified: true,
at_hash: '[REDACTED]',
name: '[REDACTED]',
picture: '[REDACTED]',
given_name: '[REDACTED]',
locale: 'en',
iat: [REDACTED],
exp: [REDACTED]
}
}Answered by Oriental Scops-Owl
Thanks for the follow-up!
As it turns out, my logic was sound, but my syntax was wrong.
I was improperly destructuring the variables in the NextAuth callbacks.
Incorrect callback 🚫
Correct callback ✅
Working NextAuth config:
I appreciate your time 🙂
As it turns out, my logic was sound, but my syntax was wrong.
I was improperly destructuring the variables in the NextAuth callbacks.
Incorrect callback 🚫
async jwt(token, user, account)Correct callback ✅
async jwt({token, user, account})Working NextAuth config:
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
const options = {
session: {
strategy: "jwt",
},
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
authorization: {
params: {
scope: "openid email profile https://www.googleapis.com/auth/youtube.readonly",
},
},
}),
],
callbacks: {
async jwt({ token, account }) {
if (account) {
token.accessToken = account.access_token;
}
return token;
},
async session({ session, token }) {
session.user = token;
session.accessToken = token.accessToken;
return session;
},
},
};
export default (req, res) => NextAuth(req, res, options);I appreciate your time 🙂
16 Replies
Oriental Scops-OwlOP
For additional context, this is my current Next Auth config:
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
const options = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
authorization: {
params: {
scope: "openid email profile https://www.googleapis.com/auth/youtube.readonly",
},
},
}),
],
callbacks: {
async jwt(token, user, account) {
try {
if (token.token.account?.access_token) {
token.accessToken = token.token.account.access_token;
}
return token;
} catch (error) {
throw error;
}
},
async session(session, token) {
try {
if (token) {
session.accessToken = token.accessToken;
}
return session;
} catch (error) {
throw error;
}
},
},
debug: true,
};
export default (req, res) => NextAuth(req, res, options);@Oriental Scops-Owl my sample code to invoke Google Calender API with Auth.js
https://github.com/tfutada/zenn-nextjs/blob/main/app/google-calendar/page.tsx
https://github.com/tfutada/zenn-nextjs/blob/main/app/google-calendar/page.tsx
YouTube with OAuth2
Oriental Scops-OwlOP
@tafutada777 Thank you for the reply! I've been working on this since I received your feedback this morning. I attempted to do something similar but I couldn't figure out how to get it to work with the Next JS pages router (which is what my app was using). Now, I've migrated my app to the app router and have configured my Next Auth to look almost exactly like your example :
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
//https://www.googleapis.com/auth/youtube.readonly
export const options = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
authorization: {
params: {
scope: "openid email profile https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/youtube.readonly",
},
},
}),
// Add other providers here if needed
],
jwt: async ({ token, user, account, profile, isNewUser }) => {
// Add role to the user info in the token right after sign in
console.log("in jwt", { user, token, account, profile });
if (user) {
token.user = user;
token.role = u.role;
}
if (account) {
token.accessToken = account.access_token;
token.refreshToken = account.refresh_token;
}
return token;
},
session: ({ session, token }) => {
console.log("in session", { session, token });
token.accessToken;
return {
...session,
user: {
...session.user,
role: token.role,
accessToken: token.accessToken,
refreshToken: token.refreshToken,
},
};
},
debug: true,
};
export default (req, res) => NextAuth(req, res, options);Unfortunately, the access token is still not showing up in my session data.
I'm assuming that you are getting back an access token at this point in your code when you log the session:
Correct?
I'm only getting back
I'm assuming that you are getting back an access token at this point in your code when you log the session:
export default async function Page() {
const session = await getServerSession(options);
const user = session?.user;
console.log("Google2", user);Correct?
I'm only getting back
Google2 {
name: 'Connor',
email: 'example@gmail.com',
image: 'exampleImg'
}Also, I thought I would have seen these logs:
But, I didn't. Any clues? Thanks again 🙂
console.log("in session", { session, token });
console.log("in jwt", { user, token, account, profile });But, I didn't. Any clues? Thanks again 🙂
u need to manually transfer accessToken from Google in jwt and session callbacks.
by default, it won't be copied.
by default, it won't be copied.
plus, if u need refreshToken, set access_type to
offlineGoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
authorization: {
params: {
prompt: "consent",
access_type: "offline",
response_type: "code",
scope: "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/calendar.readonly",
}
}
}),plus, don't forget to configure the OAuth consent screen in GCP dashboard.
to access the Google API with user credential, you need to get authorized by the user in the OAuth consent screen(popup dialog in browsers)
which return a authorized code, a short live token, with that code you will get an accessToken from Google (Auth.js does that automagically behind the scene, so you don't need it. but you might wanna know how OAuth2 works)
which return a authorized code, a short live token, with that code you will get an accessToken from Google (Auth.js does that automagically behind the scene, so you don't need it. but you might wanna know how OAuth2 works)
plus be aware the diff btw authentication and authorization in general.
authentication: Google identifies the user using email and pass (MFA)
authorization: the (identified) user allows you to access their resource(scope) in a popup dialog.
then you will get an accessToken from Google to access the scope on behave of the user.
authorization: the (identified) user allows you to access their resource(scope) in a popup dialog.
then you will get an accessToken from Google to access the scope on behave of the user.
Oriental Scops-OwlOP
Thanks for the follow-up!
As it turns out, my logic was sound, but my syntax was wrong.
I was improperly destructuring the variables in the NextAuth callbacks.
Incorrect callback 🚫
Correct callback ✅
Working NextAuth config:
I appreciate your time 🙂
As it turns out, my logic was sound, but my syntax was wrong.
I was improperly destructuring the variables in the NextAuth callbacks.
Incorrect callback 🚫
async jwt(token, user, account)Correct callback ✅
async jwt({token, user, account})Working NextAuth config:
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
const options = {
session: {
strategy: "jwt",
},
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
authorization: {
params: {
scope: "openid email profile https://www.googleapis.com/auth/youtube.readonly",
},
},
}),
],
callbacks: {
async jwt({ token, account }) {
if (account) {
token.accessToken = account.access_token;
}
return token;
},
async session({ session, token }) {
session.user = token;
session.accessToken = token.accessToken;
return session;
},
},
};
export default (req, res) => NextAuth(req, res, options);I appreciate your time 🙂
Answer