ADR-006 — Migration vers Authentik OIDC et architecture multi-utilisateurs
Date : 2026-04-25 · Statut : Accepté · Remplace : ADR-003
Contexte
ADR-003 avait établi une authentification single-user minimaliste : l'unique utilisateur était défini par les variables d'environnement ADMIN_EMAIL et ADMIN_PASSWORD_HASH, sans table de sessions ni provider OAuth. Cette approche était intentionnellement simple pour la V1.
La V2 de Patrimo prépare le multi-utilisateurs : plusieurs utilisateurs peuvent coexister en base, chacun possédant ses propres comptes, transactions et budgets. L'architecture d'auth doit donc évoluer pour :
- Permettre la création de comptes utilisateur en base (
prisma.user) - Offrir un IDP centralisé pour les environnements où plusieurs personnes utilisent la même instance (famille, équipe)
- Conserver un accès par formulaire credentials (email + mot de passe bcrypt) pour les utilisateurs sans compte OIDC
- Distinguer les utilisateurs admin (capables de créer de nouveaux comptes)
L'application est auto-hébergée sur Coolify (on-premise) — un IDP auto-hébergé est préférable à un SaaS pour garder la maîtrise des données.
Options considérées
Option A — Authentik OIDC (provider principal) + Credentials (fallback) (retenu)
- Pour : auto-hébergé, supporte OIDC standard, s'intègre nativement dans NextAuth v5 via
next-auth/providers/authentik, account linking automatique si même email, IDP mutualisable pour d'autres apps - Contre : infrastructure supplémentaire (service Authentik), 3 nouvelles variables d'env, workaround PrismaAdapter nécessaire (collision de noms de modèles)
Option B — Keycloak OIDC
- Pour : standard enterprise, très documenté
- Contre : consommation mémoire et CPU significative pour un usage personnel, configuration plus complexe, overkill pour Patrimo
Option C — Auth0 / Clerk (SaaS)
- Pour : zéro infrastructure à maintenir
- Contre : données d'identité chez un tiers, coût à l'échelle, dépendance externe incompatible avec la philosophie on-premise
Option D — Credentials seuls, multi-users
- Pour : pas de nouveau service
- Contre : pas d'SSO, pas de délégation de gestion des identités, formulaire de création d'utilisateur à sécuriser manuellement, pas adapté à la roadmap V2
Décision
Option A — Authentik OIDC comme provider principal, Credentials conservé pour tous les utilisateurs.
Authentik est déjà présent dans l'infrastructure on-premise. NextAuth v5 fournit un provider Authentik natif qui consomme les trois variables AUTHENTIK_CLIENT_ID, AUTHENTIK_CLIENT_SECRET et AUTHENTIK_ISSUER. La coexistence des deux providers avec account linking automatique (allowDangerousEmailAccountLinking: true) garantit la continuité pour les utilisateurs existants (credentials) sans forcer une migration brutale.
Le champ isAdmin sur User (booléen, défaut false) gate la création de nouveaux comptes via createUser() dans src/app/actions/users.ts.
Workaround PrismaAdapter — collision du modèle Account
Le modèle financier Account (@@map("accounts")) occupe le nom que le PrismaAdapter de NextAuth attend pour les comptes OAuth. La solution est un proxy client :
// src/lib/auth.ts:33-36
const prismaForAdapter = {
...prisma,
account: prisma.oAuthAccount,
} as unknown as Parameters<typeof PrismaAdapter>[0];
Le modèle OAuthAccount (@@map("auth_accounts")) est ainsi exposé sous l'alias account attendu par l'adapter, sans renommer le modèle financier ni toucher au schéma.
Conséquences
Positives
- Architecture multi-utilisateurs prête : chaque
Useren base possède ses propres données financières - SSO via Authentik : un seul compte pour accéder à toutes les apps de l'infrastructure
- Account linking transparent : un utilisateur credentials existant peut se connecter via OIDC sans perdre ses données
isAdminpropagé dans le JWT et la session, disponible dans tous les server actions viaauth()- PrismaAdapter gère nativement
OAuthAccount,AuthSession,VerificationToken— pas de code de session custom
Risques acceptés
passwordHashdevient nullable (String?) : les utilisateurs créés uniquement via OIDC n'ont pas de mot de passe ; ils ne peuvent pas utiliser le formulaire credentials (guard dansauthorize())- 3 nouvelles tables en base :
auth_accounts,auth_sessions,verification_tokens - Workaround
prismaForAdapter: dépend de la signature interne dePrismaAdapter— à revérifier lors des montées de version de@auth/prisma-adapter - Infrastructure supplémentaire : Authentik doit être opérationnel pour que le provider OIDC fonctionne (le provider Credentials reste disponible en fallback)
Révision
Cette décision serait à remettre en question si :
- Authentik était abandonné ou remplacé par un autre IDP dans l'infrastructure — migrer vers le nouveau provider OIDC (changement limité à
src/lib/auth.ts) - Le workaround
prismaForAdapterdevenait incompatible avec une version de@auth/prisma-adapter— envisager renommerOAuthAccountenAccountet renommer le modèle financier - L'application redevenait strictement single-user — simplifier en supprimant le provider Authentik (ADR à créer)