Skip to content

Instantly share code, notes, and snippets.

@vanling
Last active July 27, 2023 07:05
Show Gist options
  • Save vanling/b10ebe14ee90ff249bea28bf94abb745 to your computer and use it in GitHub Desktop.
Save vanling/b10ebe14ee90ff249bea28bf94abb745 to your computer and use it in GitHub Desktop.
import CredentialsProvider from 'next-auth/providers/credentials'
import { NuxtAuthHandler } from '#auth'
export default NuxtAuthHandler({
// secret needed to run nuxt-auth in production mode (used to encrypt data)
secret: useRuntimeConfig().authSecret,
providers: [
CredentialsProvider.default({
name: 'Directus',
credentials: {
email: { label: 'Email', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials, req) {
try {
const userTokens = await $fetch(
`https://${useRuntimeConfig().public.directusUrl}/auth/login`,
{
method: 'POST',
body: {
email: credentials.email,
password: credentials.password,
},
headers: {
'Content-Type': 'application/json',
},
}
)
const userDetails = await $fetch(
`https://${useRuntimeConfig().public.directusUrl}/users/me`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userTokens?.data?.access_token}`,
},
}
)
if (
!userTokens ||
!userTokens.data ||
!userDetails ||
!userDetails.data
) {
throw createError({
statusCode: 500,
statusMessage: 'Nuxt auth failed',
})
}
const user = {
id: userDetails.data.id,
email: userDetails.data.email,
firstName: userDetails.data.first_name,
name:
userDetails.data.first_name + ' ' + userDetails.data.last_name,
image: userDetails.data.avatar,
lastName: userDetails.data.last_name,
role: userDetails.data.role,
status: userDetails.data.status,
accessToken: userTokens.data.access_token,
accessTokenExpires: Date.now() + userTokens.data.expires,
refreshToken: userTokens.data.refresh_token,
}
return user
} catch (error) {
console.warn('Error logging in', error)
return null
}
},
}),
],
session: {
strategy: 'jwt',
},
callbacks: {
async signIn({ user }) {
// Do your checks here like status active and/or role checking
// const allowedRoles = [
// 'bf86376e-e657-400f-8009-93cd733cc7a5', //use role a
// 'bf86376e-e657-400f-8009-93cd733cc7a5', //use role b
// ]
// if (allowedRoles.includes(user.role) && user.status === 'active') {
// return true
// } else {
// return false
// }
return true
},
async jwt({ token, user, account }) {
if (account && user) {
// omit token info
let { accessToken, accessTokenExpires, refreshToken, ...filtered } =
user
token = {
...token,
user: filtered,
...{
access_token: accessToken,
access_token_expires: accessTokenExpires - 1000 * 60,
refresh_token: refreshToken,
},
}
}
if (
token.access_token_expires &&
Date.now() > token.access_token_expires
) {
console.log('refresh token please')
const refresh = await $fetch(
`https://${useRuntimeConfig().public.directusUrl}/auth/refresh`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
refresh_token: token.refresh_token,
mode: 'json',
},
}
)
if (!refresh || !refresh.data) {
console.warn('No refreshed tokens')
throw refresh
}
token = {
...token,
...{
access_token: refresh.data.access_token,
access_token_expires: Date.now() + refresh.data.expires - 1000 * 60,
refresh_token: refresh.data.refresh_token,
},
}
}
// console.log(token)
return token
},
async session({ session, token, user, profile }) {
session.accessToken = token.access_token
session.user = {
...session.user,
id: token.sub,
role: token.user.role,
access_token: token.access_token,
}
return session
},
},
})
@kadlinobit
Copy link

Nice example, seems like it's working better than the original one - in case when the token refresh fails, it logs the user out. Did you use this in some real project, or just made the example?
Do you know if it's possible to somehow inform frontend when the token refresh fails, so a warning message can be displayed? Thanks!

@vanling
Copy link
Author

vanling commented Jul 13, 2023

@kadlinobit Hi, thanks.
To be honest, I've used this in production before but replaced it all with https://github.com/becem-gharbi/nuxt-directus. There was just too much going on with authenticating for Directus SDK requests etc.

It has been a while but I think it came down to a composable that wrapped all the directus.items() calls and when that failed it tried to refresh token.. and when that failed again the composable triggered an modal/notification telling the user to login again.

It was getting more and more complex and then i found the nuxt-directus by Becem.. fixed al my issues. Login directly, login with providers.. uses the standard Directus SDK but replaced axios for $fetch.

@kadlinobit
Copy link

@vanling I tried couple of solutions after switching to Nuxt 3, but there was always something wrong or missing... Then I put my personal project to sleep and thought I would wait for the (long-ago) announced official nuxt-auth module. Which still didn't come any closer to be released. 😄

But this nuxt-directus module by Becem sounds like something that could solve it for me as well, I have to try it out.

Thanks so much for the info! Cheers!

@quroom
Copy link

quroom commented Jul 27, 2023

@vanling
Don't you have any problem when open browser with token expires?
I am struggling update cookie problem :(
sidebase/nuxt-auth#498
I guess the code, there is no problem.. but cookie was not updated..
Doesn't it happend to you?

If it doesn't happend, could I know which nuxt-auth and next-auth version you use?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment