/* eslint-disable new-cap */ import CredentialsProvider from 'next-auth/providers/credentials' import {readMe, withToken} from '@directus/sdk' import {directusClient} from '@/lib/directus.js' const apiUrl = process.env.DIRECTUS_API_URL const nextauthSecret = process.env.NEXTAUTH_SECRET // On rafraîchit 60s avant l'échéance réelle pour éviter les races const REFRESH_MARGIN_MS = 60 * 1000 /** * Appelle l'endpoint Directus /auth/refresh et retourne les nouveaux tokens. * Lève une erreur si le refresh échoue (refresh_token expiré ou révoqué). * * @param {string} refreshToken * @returns {Promise<{accessToken: string, refreshToken: string, accessTokenExpires: number}>} */ async function refreshDirectusToken(refreshToken) { const res = await fetch(`${apiUrl}/auth/refresh`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({refresh_token: refreshToken, mode: 'json'}), }) if (!res.ok) { throw new Error(`Directus refresh failed with status ${res.status}`) } const {data} = await res.json() return { accessToken: data.access_token, refreshToken: data.refresh_token, accessTokenExpires: Date.now() + (data.expires ?? 900_000) - REFRESH_MARGIN_MS, } } export const options = { providers: [ CredentialsProvider({ name: 'Credentials', credentials: { email: {}, password: {} }, async authorize(credentials) { const res = await fetch(`${apiUrl}/auth/login`, { method: 'POST', body: JSON.stringify(credentials), headers: {'Content-Type': 'application/json'} }) const user = await res.json() if (res.ok && user) { return user } return null } }) ], trustHost: true, secret: nextauthSecret, pages: { signIn: '/login' }, callbacks: { async jwt({token, user, account}) { // Connexion initiale : enrichissement du token avec les données Directus if (account && user) { const userData = await directusClient.request( withToken( user.data.access_token, readMe({fields: ['*']}) ) ) return { ...token, accessToken: user.data.access_token, refreshToken: user.data.refresh_token, accessTokenExpires: Date.now() + (user.data.expires ?? 900_000) - REFRESH_MARGIN_MS, user: userData, } } // Token encore valide : on le retourne sans modification if (Date.now() < token.accessTokenExpires) { return token } // Token expiré : tentative de rafraîchissement silencieux try { const refreshed = await refreshDirectusToken(token.refreshToken) return {...token, ...refreshed} } catch (refreshError) { console.error('Refresh token Directus échoué :', refreshError.message) return {...token, error: 'RefreshAccessTokenError'} } }, async session({session, token}) { session.user.userId = token.user.id session.user.username = token.user.first_name session.user.accessToken = token.accessToken session.user.refreshToken = token.refreshToken session.user.token = token.user.token session.user.email = token.user.email session.user.password = token.user.password session.error = token.error return session } } }