Pourquoi Securiser une application Next.js ?
La sécurité est un aspect crucial que tout développeur doit prendre en compte lors de la création d'applications, et cela vaut particulièrement pour les applications Next.js. En raison de sa nature moderne et réactive, Next.js apporte des défis supplémentaires à la sécurité. Dans ce tutoriel, nous allons explorer comment sécuriser une application Next.js en abordant les concepts fondamentaux, en réalisant un projet fil rouge complet et en identifiant les erreurs courantes.
Prerequis
Avant de commencer cette formation, assurez-vous que vous disposez des connaissances suivantes :
- Connaissance approfondie de JavaScript ES6+
- Compréhension des concepts React
- Familiarité avec le gestionnaire de paquets npm/yarn
- Connaissance de la structure d'un projet Next.js
Outils à installer :
- Node.js (version recommandée : 14.0.0 ou supérieure)
- npm (v6.0.0 ou supérieure)
Concepts fondamentaux
1. Authentification et Autorisation
L'authentification vérifie l'identité d'un utilisateur, tandis que l'autorisation détermine ce qu'un utilisateur peut faire une fois authentifié.
Schema mental :
+-------------------+
| Authentification|
+---------+---------+
|
v
+---------+---------+
| Autorisation |
+-------------------+
Code fonctionnel :
// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
export default NextAuth({
providers: [
Providers.Email({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}),
],
callbacks: {
async signIn(user, account, profile) {
return true;
},
async session(session, token) {
session.user.id = token.sub;
return session;
}
}
});
2. Middleware
Middleware est une fonction qui effectue des opérations avant ou après la requête est traitée par un composant.
Schema mental :
+-------------------+
| Middleware |
+---------+---------+
|
v
+---------+---------+
| Composant |
+-------------------+
Code fonctionnel :
// middleware.js
export function middleware(req, ev) {
if (req.headers.authorization !== 'Bearer mySecretToken') {
return Response.json({ message: 'Unauthorized' }, { status: 401 });
}
return NextResponse.next();
}
3. Sécurité des Cookies
Les cookies sont utilisés pour stocker des informations sur le navigateur de l'utilisateur. Il est important de les sécuriser en désactivant la transmission en clair et en définissant un chemin sécurisé.
Schema mental :
+-------------------+
| Cookies |
+---------+---------+
|
v
+---------+---------+
| Sécurité |
+-------------------+
Code fonctionnel :
// next.config.js
module.exports = {
cookies: {
secure: true,
httpOnly: true,
sameSite: 'strict',
},
};
Mise en pratique : projet fil rouge
Nous allons créer un mini-projet complet et réaliste : une application Next.js pour gérer des tâches. Voici les étapes à suivre :
- Initialiser le projet
npx create-next-app task-manager
cd task-manager
- Créer la page d'accueil
// pages/index.js
import Head from 'next/head';
export default function Home() {
return (
<div>
<Head>
<title>Task Manager</title>
</Head>
<h1>Welcome to Task Manager</h1>
</div>
);
}
- Créer le composant de liste des tâches
// components/TaskList.js
import React from 'react';
const TaskList = ({ tasks }) => {
return (
<ul>
{tasks.map(task => (
<li key={task.id}>{task.title}</li>
))}
</ul>
);
};
export default TaskList;
- Créer le composant de création de tâches
// components/CreateTask.js
import React, { useState } from 'react';
const CreateTask = ({ onAdd }) => {
const [title, setTitle] = useState('');
const handleSubmit = e => {
e.preventDefault();
onAdd({ title });
setTitle('');
};
return (
<form onSubmit={handleSubmit}>
<input type="text" value={title} onChange={e => setTitle(e.target.value)} />
<button type="submit">Create Task</button>
</form>
);
};
export default CreateTask;
- Mettre en place le state des tâches
// pages/index.js
import Head from 'next/head';
import TaskList from '../components/TaskList';
import CreateTask from '../components/CreateTask';
export default function Home() {
const [tasks, setTasks] = useState([]);
const addTask = task => {
setTasks([...tasks, { id: Date.now(), ...task }]);
};
return (
<div>
<Head>
<title>Task Manager</title>
</Head>
<h1>Welcome to Task Manager</h1>
<CreateTask onAdd={addTask} />
<TaskList tasks={tasks} />
</div>
);
}
- Ajouter un middleware pour la protection des routes sensibles
// middleware.js
export function middleware(req, ev) {
if (req.headers.authorization !== 'Bearer mySecretToken') {
return Response.json({ message: 'Unauthorized' }, { status: 401 });
}
return NextResponse.next();
}
Erreurs frequentes et debugging
1. Erreur : Invariant failed: You should not use <Route> or outside a <Router> component.
Code incorrect :
// pages/_app.js
import '../styles/globals.css';
import { useEffect } from 'react';
function MyApp({ Component, pageProps }) {
return (
<>
<Component {...pageProps} />
</>
);
}
export default MyApp;
Code correct :
// pages/_app.js
import '../styles/globals.css';
import { useEffect } from 'react';
function MyApp({ Component, pageProps }) {
return (
<>
<Component {...pageProps} />
</>
);
}
export default MyApp;
2. Erreur : Error: Error parsing JSON - Unexpected end of JSON input
Code incorrect :
// pages/api/task.js
import { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const task = req.body;
// Code pour ajouter la tâche
}
Code correct :
// pages/api/task.js
import { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === 'POST') {
const task = req.body;
// Code pour ajouter la tâche
} else {
res.status(405).json({ message: 'Method not allowed' });
}
}
3. Erreur : Error: connect ECONNREFUSED 127.0.0.1:3000
Code incorrect :
// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
export default NextAuth({
providers: [
Providers.Email({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}),
],
});
Code correct :
// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
export default NextAuth({
providers: [
Providers.Email({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}),
],
});
Pour aller plus loin
- Intégrer le stockage sécurisé des données avec Prisma
- Ajouter un système de notifications push
- Déployer l'application sur Vercel
Défi pratique : Ajoutez une fonctionnalité pour supprimer des tâches en utilisant une requête DELETE dans la route API correspondante.
Ce tutoriel a couvert les concepts fondamentaux de la sécurité d'une application Next.js et a montré comment les mettre en œuvre dans un projet réel. En suivant ces étapes, vous devriez être capable de sécuriser votre application contre les menaces courantes et de protéger vos utilisateurs.