Nouveau : Datasets open source gratuits disponibles !Decouvrir →
🔐
Intermediaire 25 min Next.js

Authentification dans Next.js

Pourquoi Authentification dans Next.js ?

L'authentification est un élément fondamental en développement web, car elle permet de protéger les ressources et les données sensibles des utilisateurs. En tant que développeur senior Next.js avec 10+ ans d'expérience, vous savez combien c'est crucial de mettre en place une authentification robuste pour votre application. Dans un contexte réel, un développeur a besoin d'une solution d'authentification pour plusieurs raisons :

  • Sécurité des données : Protéger les informations personnelles et sensibles des utilisateurs.
  • Contrôle d'accès : Permettre aux utilisateurs de se connecter à leur compte et d'effectuer des actions spécifiques.
  • Gestion des permissions : Définir les droits d'accès pour chaque utilisateur ou groupe.

Un cas d'utilisation concret serait un gestionnaire de tâches où les utilisateurs doivent être authentifiés pour voir, ajouter, modifier ou supprimer leurs propres tâches. Sans une authentification fiable, ces informations seraient vulnérables et potentiellement accessibles par n'importe qui.

Prerequis

  • Connaissances nécessaires :

    • JavaScript ES6+
    • Concepts de React
    • Compréhension de Next.js (composants, routes, API routes)
    • Familiarité avec les bases de données (ex: MongoDB, PostgreSQL)
  • Outils à installer :

    • Node.js (v14+)
    • npm (Node Package Manager)
    • MongoDB (ou une autre base de données compatible)

Concepts fondamentaux

1. Authentification vs Autorisation

Authentification : Vérifier l'identité d'un utilisateur en vérifiant ses identifiants (login/password).

Autorisation : Définir les permissions et droits d'accès une fois que l'utilisateur est authentifié.

// Exemple de structure pour authentification et autorisation
const isAuthenticated = async (req, res) => {
  // Vérifie si le token JWT est présent dans les cookies ou headers
  const token = req.cookies.token;
  if (!token) return false;

  try {
    const decoded = jwt.verify(token, 'secret-key');
    // Vérifie si l'utilisateur a les permissions nécessaires
    const userRole = getUserRole(decoded.userId);
    return userRole === 'admin';
  } catch (error) {
    return false;
  }
};

2. JWT (JSON Web Token)

JWT est un format compact et sécurisé pour transmettre des informations entre parties en tant qu'assertions JSON. Il est largement utilisé dans l'authentification.

// Exemple de génération et de vérification d'un JWT
const generateToken = (userId) => {
  return jwt.sign({ userId }, 'secret-key', { expiresIn: '1h' });
};

const verifyToken = async (token) => {
  try {
    const decoded = await jwt.verify(token, 'secret-key');
    return decoded;
  } catch (error) {
    throw new Error('Invalid token');
  }
};

3. Middleware d'authentification

Un middleware est une fonction qui s'exécute entre la requête et la réponse du serveur. Elle peut être utilisée pour vérifier l'authentification avant de permettre l'accès à certaines routes.

// Exemple de middleware d'authentification
const withAuth = (WrappedComponent) => {
  return (props) => {
    const isAuthenticated = props.isAuthenticated;
    if (!isAuthenticated) {
      return <Redirect to="/login" />;
    }
    return <WrappedComponent {...props} />;
  };
};

export default withAuth;

4. Utilisation des hooks React

Les hooks de React comme useState et useEffect peuvent être utilisés pour gérer l'état d'authentification.

// Exemple d'utilisation de useState et useEffect pour gérer l'état d'authentification
const useAuth = () => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  useEffect(() => {
    const token = localStorage.getItem('token');
    if (token) {
      verifyToken(token).then((decoded) => {
        setIsAuthenticated(true);
      });
    }
  }, []);

  return { isAuthenticated };
};

export default useAuth;

Mise en pratique : Projet fil rouge

Pour illustrer la mise en place de l'authentification dans Next.js, nous allons créer un simple gestionnaire de tâches. Ce projet comprendra les fonctionnalités suivantes :

  • Connexion et déconnexion
  • Création et affichage des tâches
  • Édition et suppression des tâches

Étape 1 : Initialisation du projet

npx create-next-app@latest task-manager
cd task-manager

Étape 2 : Installation des dépendances

npm install next-auth jsonwebtoken bcryptjs mongoose

Étape 3 : Configuration de Next-Auth

Créez un fichier pages/api/auth/[...nextauth].js :

import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
import bcrypt from 'bcryptjs';
import User from '../../../models/User';

export default NextAuth({
  providers: [
    Providers.Credentials({
      name: 'Credentials',
      credentials: {
        email: { label: 'Email', type: 'email' },
        password: { label: 'Password', type: 'password' }
      },
      async authorize(credentials) {
        const user = await User.findOne({ email: credentials.email });
        if (user && bcrypt.compareSync(credentials.password, user.password)) {
          return user;
        } else {
          return null;
        }
      }
    })
  ],
  callbacks: {
    jwt: async (token, user) => {
      if (user) {
        token.id = user._id;
      }
      return Promise.resolve(token);
    },
    session: async (session, token) => {
      session.userId = token.id;
      return Promise.resolve(session);
    }
  }
});

Étape 4 : Création du modèle Mongoose

Créez un fichier models/User.js :

import mongoose from 'mongoose';

const UserSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true }
});

export default mongoose.model('User', UserSchema);

Étape 5 : Création des composants

Créez un fichier components/AuthForm.js :

import React, { useState } from 'react';
import { useRouter } from 'next/router';

const AuthForm = ({ isLogin }) => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const router = useRouter();

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      if (isLogin) {
        // Connexion
        const response = await fetch('/api/auth/signin', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ email, password })
        });
        const result = await response.json();
        if (result.ok) {
          router.push('/');
        } else {
          alert(result.message);
        }
      } else {
        // Inscription
        const response = await fetch('/api/auth/signup', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ email, password })
        });
        const result = await response.json();
        if (result.ok) {
          router.push('/login');
        } else {
          alert(result.message);
        }
      }
    } catch (error) {
      console.error(error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} />
      <input type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} />
      <button type="submit">{isLogin ? 'Connectez-vous' : 'Inscrivez-vous'}</button>
    </form>
  );
};

export default AuthForm;

Créez un fichier pages/login.js :

import AuthForm from '../components/AuthForm';

const LoginPage = () => {
  return <AuthForm isLogin />;
};

export default LoginPage;

Créez un fichier pages/signup.js :

import AuthForm from '../components/AuthForm';

const SignupPage = () => {
  return <AuthForm isLogin={false} />;
};

export default SignupPage;

Étape 6 : Création des pages protégées

Créez un fichier pages/tasks.js et ajoutez le middleware d'authentification :

import { useEffect, useState } from 'react';
import useAuth from '../components/useAuth';

const TasksPage = () => {
  const { isAuthenticated } = useAuth();
  const [tasks, setTasks] = useState([]);

  useEffect(() => {
    if (!isAuthenticated) return;

    fetch('/api/tasks')
      .then((response) => response.json())
      .then((data) => setTasks(data));
  }, [isAuthenticated]);

  return (
    <div>
      {isAuthenticated ? (
        <ul>
          {tasks.map((task) => (
            <li key={task._id}>{task.title}</li>
          ))}
        </ul>
      ) : (
        <p>Veuillez vous connecter pour voir vos tâches.</p>
      )}
    </div>
  );
};

export default TasksPage;

Étape 7 : Création des API routes

Créez un fichier pages/api/tasks.js :

import User from '../../models/User';
import Task from '../../models/Task';

const handler = async (req, res) => {
  const { method } = req;

  if (method === 'GET') {
    // Récupérer les tâches de l'utilisateur connecté
    const token = req.headers.authorization.split(' ')[1];
    const decoded = jwt.verify(token, 'secret-key');
    const tasks = await Task.find({ userId: decoded.id });
    res.status(200).json(tasks);
  } else if (method === 'POST') {
    // Créer une nouvelle tâche
    const { title } = req.body;
    const token = req.headers.authorization.split(' ')[1];
    const decoded = jwt.verify(token, 'secret-key');
    const task = new Task({ title, userId: decoded.id });
    await task.save();
    res.status(201).json(task);
  }
};

export default handler;

Créez un fichier pages/api/tasks/[id].js :

import User from '../../models/User';
import Task from '../../models/Task';

const handler = async (req, res) => {
  const { method } = req;
  const { id } = req.query;

  if (method === 'GET') {
    // Récupérer une tâche spécifique
    const task = await Task.findById(id);
    res.status(200).json(task);
  } else if (method === 'PUT') {
    // Mettre à jour une tâche
    const { title, completed } = req.body;
    const task = await Task.findByIdAndUpdate(id, { title, completed }, { new: true });
    res.status(200).json(task);
  } else if (method === 'DELETE') {
    // Supprimer une tâche
    await Task.findByIdAndDelete(id);
    res.status(204).end();
  }
};

export default handler;

Erreurs fréquentes et debugging

1. Erreur : Token invalide

Code incorrect :

const decoded = jwt.verify(token, 'wrong-secret-key');

Code correct :

try {
  const decoded = jwt.verify(token, 'secret-key');
} catch (error) {
  throw new Error('Invalid token');
}

2. Erreur : Utilisateur non trouvé

Code incorrect :

const user = await User.findOne({ email });
if (!user) return null;

Code correct :

const user = await User.findOne({ email });
if (!user) throw new Error('User not found');

3. Erreur : Route non autorisée

Code incorrect :

if (req.method !== 'GET') {
  return res.status(405).end();
}

Code correct :

if (!isAuthenticated) return res.status(401).json({ message: 'Unauthorized' });
if (req.method === 'GET') {
  // Logique pour la méthode GET
} else if (req.method === 'POST') {
  // Logique pour la méthode POST
}

Pour aller plus loin

1. Utilisation de OAuth pour une connexion sociale

Incorporer des options de connexion via Google, Facebook ou Twitter.

2. Gestion des rôles et permissions

Définir différents niveaux d'accès et limiter certaines actions en fonction du rôle de l'utilisateur.

3. Sécurité supplémentaire

Utiliser HTTPS, ajouter des limites d'IP, configurer CORS appropriéement.

Défi pratique

Développer un système de notifications pour les utilisateurs lorsqu'ils créent ou modifient une tâche. Utilisez Next-Auth pour la gestion des sessions et MongoDB pour stocker les notifications.

Besoin d'aide sur Next.js ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Comment installer l'authentification NextAuth.js dans mon projet Next.js ?
Pour installer NextAuth.js, exécutez la commande 'npm install next-auth'. Ensuite, vous pouvez initialiser le processus d'authentification en suivant les instructions fournies dans la documentation officielle.
Quelles sont les principales méthodes d'authentification supportées par NextAuth.js ?
NextAuth.js supporte plusieurs méthodes d'authentification, y compris Google, Facebook, Twitter, Email/Password, et OAuth. Vous pouvez choisir la méthode qui convient le mieux à vos besoins.
Comment protéger les routes en utilisant l'authentification dans Next.js avec NextAuth.js ?
Pour protéger une route, utilisez le middleware d'autorisation fourni par NextAuth.js. Vous pouvez ajouter un fichier `[...].js` dans le dossier `pages/api/auth/[...nextauth]` et configurer les permissions nécessaires.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.