How to implement Facebook OIDC login on Next.js with NextAuth? - facebook

I have implemented a regular (Auth2) Facebook login with NextAuth that works fine, but now, I have to switch to OIDC login and can't seem to implement it properly. I'm getting the following error message:
[token_endpoint must be configured on the issuer](https://next-auth.js.org/errors#callback_oauth_error token_endpoint must be configured on the issuer TypeError: token_endpoint must be configured on the issuer)
And this is my configuration on the Facebook Provider:
export default NextAuth({
providers: [
FacebookProvider({
idToken: true,
clientId: process.env.FBID,
clientSecret: process.env.FBSECRET,
wellKnown: "https://www.facebook.com/.well-known/openid-configuration",
token: {
url: "https://www.facebook.com/v11.0/dialog/oauth",
params: { scope: "openid email public_profile" },
},
}),
],
secret: "sunSAd2RkCajg2DLR3+5MfsinFwws8ZuzfPm2C+FXkc=",
});

Try this code
providers: [
FacebookProvider({
idToken: true,
clientId: process.env.FACEBOOK_CLIENT_ID,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
authorization: {
url: "https://www.facebook.com/v11.0/dialog/oauth",
params: {
client_id: process.env.FACEBOOK_CLIENT_ID,
scope: "openid email",
response_type: "code",
},
},
// wellKnown: "https://www.facebook.com/.well-known/openid-configuration/",
token: {
url: "https://graph.facebook.com/oauth/access_token",
async request(context) {
try {
// request to https://graph.facebook.com/oauth/access_token?code=""&client_id=""&redirect_uri=""&client_secret=""
const response = await axios.get(this.url, {
params: {
code: context.params.code,
client_id: context.provider.clientId,
redirect_uri: context.provider.callbackUrl,
client_secret: context.provider.clientSecret,
},
});
const tokens = response.data;
return { tokens };
} catch (error) {
throw error;
}
},
},
}),
]

Related

How to use refresh_token in #nuxtjs/auth-next?

I'm sorry if there are any mistakes because my native language is not English.
I'm using this module with the configuration below, but when I run auth/rerfresh it's requesting using the access_token.
I get the Authorization header on the server side, and when I decode the JWT, the content is access_token.
I am assuming that with this module, when the access_token expires, it will set the refresh_token in the Authorization header and run api/auth/refresh.
Please let me know if there are any mistakes...
auth: {
redirect: {
login: '/login',
logout: '/login',
callback: '/login',
home: '/'
},
strategies: {
local: {
scheme: 'refresh',
autoLogout: true,
token: {
property: 'access_token',
maxAge: 1800,
global: true,
// type: 'Bearer'
},
refreshToken: {
property: 'refresh_token',
data: 'refresh_token',
maxAge: 60 * 60 * 24 * 30
},
user: {
property: false,
autoFetch: true
},
endpoints: {
login: {
url: '/auth/login',
method: 'post',
propertyName: 'access_token',
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"grant_type": "password"
},
},
refresh: { url: '/auth/refresh', method: 'get' },
logout: { url: '/auth/logout', method: 'post', },
user: { url: '/auth/me', method: 'get', propertyName: false }
}
},
[/auth/login response]
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNjc0NTI1OTA3LCJzdGFmZl9pZCI6ImFiY2RlMTIzIn0.68BPtgr93lwHgSfSQxieEJUJtGPe9bafQMpnbdHEqy0",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaF90b2tlbiIsImV4cCI6MTY3NzExNzg0Nywic3RhZmZfaWQiOiJhYmNkZTEyMyJ9.71B1iofZIsoaduUOH7ahuTi2gc2NCp5fpsRrsZaGPMg",
"token_type": "bearer"
}
[Cookies]
Name
Value
Expires/Max-Age
Priority
auth._token_expiration.local
1674525907000
Session
Medium
auth._refresh_token.local
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaF90b2tlbiIsImV4cCI6MTY3NzExNzg0Nywic3RhZmZfaWQiOiJhYmNkZTEyMyJ9.71B1iofZIsoaduUOH7ahuTi2gc2NCp5fpsRrsZaGPMg
Session
Medium
auth._token.local
Bearer%20eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzX3Rva2VuIiwiZXhwIjoxNjc0NTI1OTA3LCJzdGFmZl9pZCI6ImFiY2RlMTIzIn0.68BPtgr93lwHgSfSQxieEJUJtGPe9bafQMpnbdHEqy0
Session
Medium
auth._refresh_token_expiration.local
1677117847000
Session
Medium
auth.strategy
local
Session
Medium
I have tried various auth settings in nuxt.config.js but could not solve the problem.
(For example, autoLogout: false, etc.)
I tried running this.$auth.refreshTokens() but , Authorization header seems to be set to access_token.
As you can see in the image above, the token was received and seems to be stored in the cookie.
refresh: { url: '/auth/refresh', method: 'post' },
Changed as above.
I expected refresh_token to be included in the Authorization header, but it seems to be included in the request body.
Changed to refer to the request body in server-side processing.

useSession stays in loading state after sign-in using REST API

I am using the Credentials provider and next-auth 4.3.1
I go to page /protected
This page does useSession({ required: true, onUnauthenticated: () => router.push('/login?redirect=/protected') })
I login on the login page I got redirected too with this code:
const { data: { csrfToken } } = await axios.get('/api/auth/csrf');
const res = await axios
.post(
'/api/auth/callback/credentials',
{
json: true,
csrfToken,
redirect: false,
email: form.email,
password: form.password
},
{
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}
);
router.push(router.query.redirect || '/');
After succesfully logging in, notice it pushes back router.push(router.query.redirect), so it takes me back to /protected
However useSession returns { data: undefined, status: loading } and triggers onUnauthenticated, taking me back to the login page
Now, I don't login again, I just type in URL bar https://localhost:3000/protected it will load the protected page and useSession properly finds the logged-in session.
Is there something I have to do to make useSession see signIn was just called?
Here is my [...nextauth].ts:
const handler = NextAuth({
secret: process.env.NEXTAUTH_SECRET,
session: {
strategy: 'jwt'
},
debug: process.env.NODE_ENV === 'development',
providers: [
CredentialsProvider({
credentials: {
email: { label: 'Email', type: 'text' },
password: { label: 'Password', type: 'password' }
},
async authorize(credentials, req) {
////// removed
}
})
],
pages: {
signIn: '/login'
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.user = { id: user.id };
}
return token;
},
async session({ session, token }) {
if (session?.user) {
session.user.id = token.user.id;
}
return session;
}
}
});

Nuxt Auth uses access token instead of refresh token

My issue is as follows:
After logging in my acces token and refresh token are stored and I can access them with this.$auth.strategy.token.get()
When my access token is expired, my app calls the /token/refresh endpoint that I created in my backend. However, instead of sending the refresh_token, nuxt auth uses the expired access token. How do I tell Nuxt auth to use the refresh token by default?
My nuxt.config.js:
auth: {
localStorage: false,
redirect: {
login: "/login/",
logout: "/",
home: "/search/",
},
watchLoggedIn: true,
strategies: {
local: {
scheme: 'refresh',
token: {
property: 'access_token',
global: true
},
refreshToken: {
property: 'refresh_token',
data: 'refresh_token',
tokenRequired: true,
maxAge: 60 * 60 * 24 * 30,
grantType: 'refresh_token'
},
user:{
property: false,
// autoFetch: false
},
endpoints: {
login: { url: '/login', method: 'post' },
refresh: { url: '/token/refresh', method: 'get', grant_type:'refresh_token'},
logout: false,
user: { url: '/currentuser', method: 'get', propertyName: false }
},
tokenRequired: true,
tokenType: 'Bearer',
}
}
}

NextAuth - AccessToken not refreshed with MongoDB and Coinbase

I have a problem with my authentication via Coinbase (using Nextauth) on NextJS app.
I made this code below, and it saves the profile well in my Mongodb database. But when I re-login, accesstoken and refreshtoken are not changed...
So I can’t use the APIs afterwards.
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
async function refreshAccessToken(token) {
try {
const url =
"https://api.coinbase.com/oauth/token?" +
new URLSearchParams({
client_id: process.env.COINBASE_CLIENT_ID,
client_secret: process.env.COINBASE_SECRET_ID,
grant_type: "refresh_token",
refresh_token: token.refreshToken,
})
const response = await fetch(url, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
method: "POST",
})
const refreshedTokens = await response.json()
if (!response.ok) {
throw refreshedTokens
}
return {
...token,
accessToken: refreshedTokens.access_token,
accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000,
refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, // Fall back to old refresh token
}
} catch (error) {
console.log(error)
return {
...token,
error: "RefreshAccessTokenError",
}
}
}
export default NextAuth({
// Configure one or more authentication providers
providers: [
Providers.Coinbase({
clientId: process.env.COINBASE_CLIENT_ID,
clientSecret: process.env.COINBASE_SECRET_ID,
callbackUrl: process.env.COINBASE_CALLBACKURL,
scope: "wallet:accounts:read",
}),
],
callbacks: {
async jwt({ token, user, account, profile, isNewUser }) {
// Initial sign in
if (account && user) {
return {
accessToken: user.data.access_token,
accessTokenExpires: Date.now() + user.data.expires_in * 1000,
refreshToken: user.data.refresh_token,
user,
}
}
// Return previous token if the access token has not expired yet
if (Date.now() < token.accessTokenExpires) {
return token
}
// Access token has expired, try to update it
return refreshAccessToken(token)
},
async session(session, token) {
session.accessToken = token.accessToken
return session
}
},
events: {
async signIn(message) { console.log('success signin') },
async signOut(message) { console.log('success signout') },
async createUser(message) { console.log('success user create') },
async updateUser(message) { console.log('success update user') },
async session(message) { console.log('success session') },
async error(message) { console.log('error') }
},
// A database is optional, but required to persist accounts in a database
database: `mongodb+srv://${process.env.NOSQL_USER}:${process.env.NOSQL_PWD}#${process.env.NOSQL_HOST}/${process.env.NOSQL_TABLE}`,
});
I’m still a beginner on NextJS and React in particular:) Thanks for your help

Cannot get a response from PostgreSQL server

I freely admit that I am completely new to next-auth and the documentation for Credentials is understandably very light. I have got the email link process to work perfectly and will be moving users across top this.
Unfortunately, I have a lot of user data that will require credentials to login and, after spending a few days getting nowhere, I just want to get some idea of what I am doing wrong! This is my [...nextauth].js file:
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
import axios from 'axios'
const options = {
providers: [
Providers.Email({
server: {
host: process.env.EMAIL_SERVER_HOST,
port: process.env.EMAIL_SERVER_PORT,
auth: {
user: process.env.EMAIL_SERVER_USER,
pass: process.env.EMAIL_SERVER_PASSWORD
}
},
from: process.env.EMAIL_FROM
}),
Providers.Credentials({
credentials: {
mem_num: { label: "Membership Number", type: "text", placeholder: "12345" },
password: { label: "Password", type: "text" }
},
authorize: async (credentials) => {
console.log("credentials: ", credentials)
try {
const data = {
mem_num: credentials.mem_num,
password: credentials.password
}
const user = await login(data)
if (user) {
console.log('user:', user)
return user
}
} catch (error) {
if (error.response) {
console.log(error.response)
Promise.reject(new Error('Invalid Number and Password combination'))
}
}
}
})
],
site: process.env.NEXTAUTH_URL || "http://localhost:3000",
database: process.env.DATABASE_URL,
session: {
// Use JSON Web Tokens for session instead of database sessions.
// This option can be used with or without a database for users/accounts.
// Note: `jwt` is automatically set to `true` if no database is specified.
jwt: true,
},
}
const login = async data => {
var config = {
headers: {
'Content-Type': "application/json; charset=utf-8",
'corsOrigin': '*',
"Access-Control-Allow-Origin": "*"
}
};
const url = process.env.DATABASE_URL;
const result = await axios.post(url, data, config);
console.log('result', result);
return result;
};
export default (req, res) => NextAuth(req, res, options);