Next-auth
Permet de gérer l'authentification sur une application nextJS en utilisant différent Provider. A l'avantage d'utiliser la session nextJS.
Installation
npm install next-auth@beta @types/next-auth
Configuration
import NextAuth from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; import HttpService from "@/services/HttpService"; import API_URLS from "@/constants/ApiUrls"; import { decodeJwt } from "jose"; interface AuthToken { accessToken: string; refreshToken: string; user: any; } async function refreshAccessToken(token: AuthToken) { try { const refreshedToken = await HttpService.post(API_URLS.refreshToken, { refreshToken: token.refreshToken, }); if (!refreshedToken || !refreshedToken.accessToken) { throw new Error("Refresh token failed"); } return { ...token, accessToken: refreshedToken.accessToken, accessTokenExpires: Date.now() + refreshedToken.expiresIn * 1000, refreshToken: refreshedToken.refreshToken ?? token.refreshToken, }; } catch (error) { console.error("Erreur lors du rafraîchissement du token", error); return { ...token, error: "RefreshAccessTokenError" }; } } // @ts-ignore export const authConfig = { pages: { signIn: '/login', signOut: "/logout", }, providers: [ CredentialsProvider({ id: "credentials", name: "Credentials", credentials: { username: { label: "Login", type: "text" }, password: { label: "Password", type: "password" }, }, async authorize(credentials) { const resp = await HttpService.post(API_URLS.authLogin, { username: credentials.username, password: credentials.password, }); if (resp.ok) { throw new Error("Invalid credentials"); } const token=resp.data; const user = decodeJwt(token.accessToken); return { id: user.sub, name: user.name, email: user.email, role: user.role, accessToken: token.accessToken, //refreshToken: token.refreshToken, accessTokenExpires: Date.now() + token.expiresIn * 1000, }; }, }), ], callbacks: { async jwt({ token, user }) { if (user) { return { accessToken: user.accessToken, //refreshToken: user.refreshToken, accessTokenExpires: Date.now() + 1000 * 60 * 60, // 1h user: { id: user.id, name: user.name, email: user.email, role: user.role }, }; } if (Date.now() < token.accessTokenExpires) { return token; } return refreshAccessToken(token); }, async session({ session, token }) { console.log("session in session method", session); console.log("token in session method", token); return { ...session, user: { ...session.user, id: token.user.id, name: token.user.name, email: token.user.email, accessToken: token.accessToken, //refreshToken: token.refreshToken, }, }; }, }, session: { strategy: 'jwt', }, secret: process.env.NEXTAUTH_SECRET as string, } satisfies NextAuthConfig; //@ts-ignore export const {handlers, auth, signIn, signOut} = NextAuth(authConfig);
Route api
Toutes les requêtes NextAuth passeront par cette route : src/app/api/auth/[…nextauth]/route.tsx
import {handlers} from "@/auth"; export const {GET, POST} = handlers;
Lib
Fichier utilitaire pour simplifier connexion et déconnexion dans src/lib/actions.ts
:
'use server'; import {signIn, signOut} from "@/auth"; interface LoginFormValues { username: string; password: string; rememberMe?: boolean; }; export const submitLogin = async (formData: LoginFormValues): Promise<any> => { await signIn('credentials', {...formData, redirectTo: '/', redirect: true}); }; export const submitLogout = async () => { await signOut({redirect: true, redirectTo: '/logout'}); };
LoginForm
Exemple de composant pour le login :
'use client'; import {Button, Checkbox, Form, Input} from "antd"; import {submitLogin} from "@/lib/actions"; export default function SignInComponent(){ const onFinish = async (values: any) => { await submitLogin(values); }; const onFinishFailed = (errorInfo: any) => { console.log('Failed:', errorInfo); }; return <div> <Form name="basic" labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} style={{ maxWidth: 600 }} initialValues={{ remember: true }} onFinish={onFinish} onFinishFailed={onFinishFailed} autoComplete="off" > <Form.Item label="Username" name="username" rules={[{ required: true, message: 'Please input your username!' }]} > <Input /> </Form.Item> <Form.Item label="Password" name="password" rules={[{ required: true, message: 'Please input your password!' }]} > <Input.Password /> </Form.Item> <Form.Item name="remember" valuePropName="checked" label={null}> <Checkbox>Remember me</Checkbox> </Form.Item> <Form.Item label={null}> <Button type="primary" htmlType="submit"> Submit </Button> </Form.Item> </Form> </div> }
Configuration
Dans le fichier .env
ou .env.local
, définir la variable NEXTAUTH_URL
:
NEXTAUTH_URL=http://127.0.0.1:3000
Génération du secret pour encodage/décodage JWT :
npx auth secret
Le secret est généré et inséré dans le fichier .env.local
.
Usage
Accès à la session nextAuth :
Côté serveur
import {auth} from "@/auth"; const session= await auth(); console.log(session.user); //user connecté
Côté client
'use client'; import {useSession} from "next-auth/react"; const userData=useSession(); console.log(userData.data?.user); //user connecté