Authentification
NextAuth v5 avec Google, Microsoft et Credentials. JWT, encryption, rate limiting.
Vue d'ensemble
HeartCo utilise NextAuth v5 (Auth.js) avec une stratégie JWT (pas de sessions en base). La session dure 24 heures.
Fichier principal : src/server/auth/config.ts
Providers
1. Google OAuth
GOOGLE_CLIENT_ID="xxxx.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="GOCSPX-xxxx"Configuration dans la Google Cloud Console :
- Créer un projet → APIs & Services → Credentials → OAuth 2.0 Client ID
- Authorized redirect URI :
http://localhost:3000/api/auth/callback/google - En production :
https://app.votre-domaine.fr/api/auth/callback/google
2. Microsoft Entra ID
MICROSOFT_CLIENT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
MICROSOFT_CLIENT_SECRET="xxxx"
MICROSOFT_TENANT_ID="common"Configuration dans le portail Azure :
- Azure AD → App registrations → New registration
- Redirect URI :
http://localhost:3000/api/auth/callback/microsoft-entra-id - Type : Web
Note : HeartCo bloque automatiquement les comptes Microsoft personnels (Consumer Tenant). Seuls les comptes organisationnels sont acceptés.
3. Credentials (Email + Mot de passe)
Authentification classique par email/mot de passe avec :
- Rate limiting : 10 tentatives par IP toutes les 15 minutes (Upstash Redis)
- Protection timing attack :
bcrypt.compare()exécuté même si l'utilisateur n'existe pas - Normalisation email : lowercase + trim automatique
- Vérification email : obligatoire avant connexion
Flow d'inscription
Register (/register)
│
▼
Création du compte + Organisation
│
▼
Email de vérification (Resend)
│
▼
Verify Email (/verify-email)
│
▼
Onboarding (/onboarding)
│ - Nom de l'organisation
│ - Secteur d'activité
│ - Configuration initiale
▼
Dashboard (/dashboard)
Pour les providers OAuth (Google, Microsoft), l'email est automatiquement vérifié et l'utilisateur passe directement à l'onboarding.
Rôles
HeartCo définit 7 rôles avec une hiérarchie stricte :
| Rôle | Niveau | Description |
|---|---|---|
ADMIN | 0 | Accès total à l'organisation |
DIRECTION | 1 | Direction — lecture étendue + validation |
MANAGER | 2 | Gestion opérationnelle (factures, devis, clients) |
HR | 3 | Ressources humaines (congés, fiches de paie) |
ACCOUNTANT | 3 | Comptabilité (FEC, rapprochements) |
COLLABORATOR | 4 | Accès limité (ses propres données) |
CLIENT | 99 | Portail client uniquement (aucune permission interne) |
Les rôles HR et ACCOUNTANT sont au même niveau hiérarchique (3) mais avec des permissions différentes. Voir Permissions.
Routes API d'authentification
| Route | Méthode | Description |
|---|---|---|
/api/auth/[...nextauth] | GET/POST | Handlers NextAuth (login, callback, session) |
/api/auth/accept-invite | POST | Accepter une invitation d'équipe |
/api/auth/forgot-password | POST | Demande de réinitialisation de mot de passe |
/api/auth/reset-password | POST | Réinitialiser le mot de passe (avec token) |
/api/auth/verify-email | GET | Vérifier l'adresse email (lien dans l'email) |
/api/auth/resend-verification | POST | Renvoyer l'email de vérification |
/api/auth/mobile/login | POST | Connexion depuis l'app mobile (JWT) |
/api/auth/mobile/refresh | POST | Rafraîchir le token mobile |
/api/auth/mobile/delete-account | DELETE | Supprimer le compte (app mobile) |
Sécurité
Token Encryption
Les tokens OAuth (Google, Microsoft) sont chiffrés au repos avec AES-256-GCM avant d'être stockés en base :
TOKEN_ENCRYPTION_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# 64 caractères hex (32 bytes) — Générer : openssl rand -hex 32Rate Limiting
Les tentatives de connexion par Credentials sont limitées à 10 par IP sur 15 minutes :
- Backend : Upstash Redis (sliding window)
- Fallback : compteur en mémoire si Redis indisponible
Auto-refresh des tokens OAuth
Les tokens Google et Microsoft sont automatiquement rafraîchis s'ils expirent dans les 5 prochaines minutes. Aucune intervention de l'utilisateur n'est nécessaire.
Protection contre les redirections ouvertes
Le callback NextAuth vérifie que l'URL de redirection est sur le même domaine (same-origin). Les redirections externes sont bloquées.
Session
La session JWT contient :
interface Session {
user: {
id: string; // ID utilisateur
name: string;
email: string;
image: string | null;
role: string; // Rôle dans l'organisation
organizationId: string | null; // ID de l'organisation
emailVerified: boolean;
emailAccountId?: string | null;
};
}Accès côté serveur :
// Dans un router tRPC
const userId = ctx.session.user.id;
const orgId = ctx.session.user.organizationId;
const role = ctx.session.user.role;Accès côté client :
import { useSession } from "next-auth/react";
const { data: session } = useSession();
console.log(session?.user.role);Ajouter un nouveau provider OAuth
-
Installer le provider (si nécessaire) — NextAuth v5 inclut les providers courants.
-
Ajouter les variables d'environnement dans
.envetsrc/env.js:
// src/env.js — section server
NEW_PROVIDER_CLIENT_ID: z.string().optional(),
NEW_PROVIDER_CLIENT_SECRET: z.string().optional(),- Configurer le provider dans
src/server/auth/config.ts:
import NewProvider from "next-auth/providers/new-provider";
providers: [
// ... providers existants
NewProvider({
clientId: env.NEW_PROVIDER_CLIENT_ID,
clientSecret: env.NEW_PROVIDER_CLIENT_SECRET,
}),
],-
Ajouter le bouton dans la page de login (
src/app/login/page.tsx). -
Tester : Vérifiez le flow complet (login → callback → session → dashboard).