Comment créer une route protégée

Apprends à sécuriser tes routes dans React Router 7 en gérant l’authentification côté serveur et en évitant les failles de sécurité.

4 min read
Déverrouillez votre potentiel

avec React Router 7

Vous en avez marre de...

❌ perdre du temps à chercher des informations éparpillées
❌ ne pas avoir de retour sur votre progression
Assistant IA spécialisé

Posez vos questions 24/7 à notre IA experte en React Router 7

Quiz interactifs

Validez vos acquis avec des quiz personnalisés et un feedback instantané

9 modules
45 leçons
Accès à vie
299.00
-50%

Pourquoi protéger une route ?

Toutes les pages ne se valent pas :

- un tableau de bord, - un formulaire de paiement, - des paramètres de compte ...

Ces écrans doivent être accessibles uniquement aux utilisateurs authentifiés. Dans React Router 7 tu peux tester la session directement dans chaque route ; mais recopier le même if (!user) redirect(...) devient vite pénible et risqué : la première omission est une faille de sécurité.

Dans cette leçon tu vas :

  1. détecter l’utilisateur depuis le cookie de session ;
  2. transformer ce test en helper réutilisable ;
  3. l’appeler aussi bien dans les loader que dans les action ;
  4. comprendre le flux complet : redirection, affichage et gestion d’erreur.

Lire la session côté serveur

Le module précédent a posé le cookie __session avec l’userId à la connexion. Créons un utilitaire pour le récupérer :

app/server/sessions.server.ts
1
import { createCookieSessionStorage } from "react-router"
2
import { prisma } from "./db.server"
3
4
type SessionData = { userId: string }
5
6
const { getSession, commitSession, destroySession } =
7
createCookieSessionStorage<SessionData>({
8
cookie: {
9
name: "__session",
10
sameSite: "lax",
11
httpOnly: true,
12
path: "/",
13
secrets: [process.env.SESSION_SECRET!], // ➜ prod only
14
secure: process.env.NODE_ENV === "production",
15
},
16
})
17
18
export async function getOptionalUser({ request }: { request: Request }) {
19
const session = await getSession(request.headers.get("Cookie"))
20
const id = session.get("userId") as string | undefined
21
if (!id) return null
22
23
// vérif anti-forgery : l’id existe *vraiment* en base
24
return prisma.user.findUnique({
25
where: { id: Number(id) },
26
select: { id: true, firstName: true, lastName: true },
27
})
28
}

getOptionalUser renvoie soit l’utilisateur, soit null. C’est idéal pour afficher un bouton Login lorsque personne n’est connecté.


Créer requireUser : la garde de sécurité

Le profil, la facturation, l’administration… doivent être fermés : pas d’utilisateur ⇒ pas d’accès. On factorise ce comportement dans une seule fonction :

app/server/sessions.server.ts
1
import { redirect } from "react-router"
2
3
export async function requireUser({ request }: { request: Request }) {
4
const user = await getOptionalUser({ request })
5
if (!user) {
6
throw redirect("/login") // ⬅️ stoppe le loader et redirige
7
}
8
return user
9
}

Remarque : redirect() lève déjà une Response HTTP 302, React Router capture l’exception et envoie le bon header au navigateur – aucun composant d’erreur n’est rendu.


L’utiliser dans un loader

app/routes/profile.tsx
1
import type { LoaderFunctionArgs } from "react-router"
2
import { requireUser } from "~/server/sessions.server"
3
4
export async function loader({ request }: LoaderFunctionArgs) {
5
// 🔒 bloque d’emblée les visiteurs
6
const user = await requireUser({ request })
7
return { user }
8
}

Visiteur anonyme ? Il est téléporté sur /login. Utilisateur connecté ? Le loader continue et fournit le profil.


…et dans une action

Les mutations POST/PUT/DELETE doivent aussi être protégées :

{5,7} app/routes/profile.tsx
1
import type { ActionFunctionArgs } from "react-router"
2
3
export async function action({ request }: ActionFunctionArgs) {
4
const user = await requireUser({ request }) // 🔒 encore
5
const form = await request.formData()
6
// … mise à jour du profil …
7
return null
8
}

Sans ce garde-fou un utilisateur malveillant pourrait envoyer une requête curl -X POST /profile et modifier n’importe quelle donnée.


Éviter la duplication côté client

La racine de l’app (app/root.tsx) charge toujours ; profitons-en pour y remplir l’objet utilisateur une seule fois :

app/root.tsx
1
export async function loader({ request }: LoaderFunctionArgs) {
2
const user = await getOptionalUser({ request })
3
return data({ user })
4
}

Puis expose-le via un hook maison :

app/root.tsx
1
import { useRouteLoaderData } from "react-router"
2
3
export function useOptionalUser() {
4
const data = useRouteLoaderData<typeof loader>("root")
5
return data?.user ?? null
6
}

Maintenant n’importe quelle page fait simplement :

app/routes/index.tsx
1
const user = useOptionalUser()

sans requête supplémentaire.


Résumé du flux complet

1
sequenceDiagram
2
participant B as Navigateur
3
participant SSR as Loader
4
participant DB as DB Prisma
5
6
B->>SSR: GET /profile (cookies?)
7
SSR->>SSR: requireUser()
8
SSR->>DB: SELECT user WHERE id = cookie
9
alt user trouvé
10
SSR-->>B: 200 + HTML
11
else pas de user
12
SSR-->>B: 302 Redirect /login
13
end
  1. Le cookie arrive dans l’entête Cookie.
  2. requireUser lit le cookie, teste l’existence en BDD.
  3. ✅ Présent : le rendu continue. ❌ Absent : throw redirect("/login"), aucune autre ligne ne s’exécute.

Points clés

  • getOptionalUser lit l’id en cookie et vérifie qu’il existe en base.
  • requireUser encapsule la redirection ; on l’appelle dans chaque loader et action à sécuriser.
  • Utilise throw redirect() – pas besoin de return.
  • Charge l’utilisateur une seule fois dans root.tsx ; expose-le avec useOptionalUser() pour éviter les requêtes redondantes.
  • La sécurité se joue côté serveur : même un fetch manuel doit passer par la garde.
Premium
Quiz interactif
Testez vos connaissances et validez votre compréhension du module avec notre quiz interactif.
1

Comprendre les concepts fondamentaux

Quelle est la principale différence entre les composants client et serveur dans React ?

Les composants client s'exécutent uniquement dans le navigateur
Les composants serveur peuvent utiliser useState
Les composants client sont plus rapides
Il n'y a aucune différence significative
2

Optimisation des performances

Quelle technique est recommandée pour éviter les rendus inutiles dans React ?

Utiliser React.memo pour les composants fonctionnels
Ajouter plus d'états locaux
Éviter d'utiliser les props
Toujours utiliser les class components
3

Architecture des données

Quel hook permet de gérer les effets de bord dans un composant React ?

useEffect
useState
useMemo
useContext
4

Gestion des erreurs

Comment implémenter la gestion des erreurs pour les requêtes API dans React ?

Utiliser try/catch avec async/await
Ignorer les erreurs
Toujours afficher un message d'erreur
Rediriger l'utilisateur
5

Déploiement et CI/CD

Quelle est la meilleure pratique pour déployer une application React en production ?

Utiliser un service CI/CD comme GitHub Actions
Copier les fichiers manuellement via FTP
Envoyer le code source complet
Ne jamais mettre à jour l'application

Débloquez ce quiz et tous les autres contenus premium en achetant ce cours