Nuxt Auth uses access token instead of refresh token - jwt

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',
}
}
}

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.

NextAuth + TwitterProvider + FirestoreAdapter => Missing Screen Name

I'm using next-auth along with next-auth/firebase-adapter packages to log to my web app via Twitter. The flow is working nicely, but I can not obtain the user's screenname (aka handle).
Before using next-auth I've been using Firebase auth(). In their package, the user's screen name has always been available.
I've been playing around with NextAuthOptions but to no avail.
Does anyone know how to make add the user's screenname to the session?
export const authOptions: NextAuthOptions = {
session: {
strategy: 'jwt',
},
adapter: FirestoreAdapter(firebaseConfig),
pages: {
signIn: '/sign-in',
},
callbacks: {
session: ({ session, token }) => {
return {
...session,
user: {
...session.user,
id: token.sub!,
},
}
},
},
events: {
createUser: async ({ user }) => {
const ref = admin
.firestore()
.collection(Constants.UsersTable)
.doc(user.id)
await ref.set(
{
screenName: <MISSING>,
daily_target: 0,
onboarded: false,
},
{ merge: true }
)
},
},
providers: [
TwitterProvider({
clientId: process.env.TWITTER_AUTH_CLIENTID,
clientSecret: process.env.TWITTER_AUTH_CLIENTSECRET,
version: '2.0',
}),
],
}
Instead of obtaining the screenname I decided to obtain the providerAccountToken and add it to my JWT token with a next-auth callback.
This is even better as I use the unique user id instead of a public handle (which the user might change in the future).
callbacks: {
jwt: ({ token, account = {} }) => {
// Persist the OAuth access_token to the token right after signin
if (account.providerAccountId) {
token.providerAccountId = account.providerAccountId
}
return token
},
},

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;
}
}
});

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

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;
}
},
},
}),
]

[Nuxt.JS]access the $auth object in the context from plugin js

I want to access the $auth object in the context from the js defined under 'plugins/', but I can't.
https://auth.nuxtjs.org/api/auth.html#auth
This module globally injects $auth instance, meaning that you can access it anywhere using this.$auth. For plugins, asyncData, fetch, nuxtServerInit and Middleware, you can access it from context.$auth
It is described above, but my code (axios-interceptor.js) cannot access $auth from context (it is undefined).
What does it take to be able to access it?
plugins/axios-interceptor.js
export default function (context) {
const { $axios, route, redirect } = context
$axios.interceptors.response.use(
function (response) {
return response
},
function (error) {
const code = parseInt(error.response && error.response.status)
const thisRoutePath = route.path
if ([401, 403].includes(code)) {
if (thisRoutePath !== '/') {
redirect('/?login')
}
}
return Promise.reject(error)
}
)
}
nuxt.config.js
export default {
plugins: [
'#/plugins/axios-interceptor.js'
],
modules: [
'#nuxtjs/axios',
'#nuxtjs/proxy',
'#nuxtjs/auth'
],
axios: {
baseURL: BASE_URL
},
auth: {
cookie: false,
autoFetchUser: false,
redirect: {
login: '/login',
logout: '/login',
callback: '/callback',
home: '/home'
},
strategies: {
local: {
endpoints: {
login: { url: BACKEND_API_PATH_BASE + '/api/v1/login/', method: 'post', propertyName: 'token' },
user: { url: BACKEND_API_PATH_BASE + '/api/v1/users/me', method: 'get', propertyName: false },
logout: false
},
},
}
},
router: {
middleware: [
'auth'
]
},
The reason I want to access $auth in axios-interceptor.js is that I want to execute $auth.logout() in the if ([401, 403].includes(code)) { block and remove the token.
I am now able to access $auth by doing the following
export default {
// plugins: [
// '#/plugins/axios-interceptor.js' ########### REMOVE ###########
// ],
:
(Ommit)
:
auth: {
:
(Ommit)
:
plugins: [
'#/plugins/axios-interceptor.js' // ########### ADD ###########
]
},
(Ommit)
:
}
The things I needed to do were listed below.
https://auth.nuxtjs.org/recipes/extend.html