Major changes across backend, frontend, infrastructure: - Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud) - Admin panel: user management, pricing, settings - Glossary system with CSV import/export - Subscription and tier quota management - Security hardening (rate limiting, API key auth, path traversal fixes) - Docker compose for dev, prod, and IONOS deployment - Alembic migrations for new tables - Frontend: dashboard, pricing page, landing page, i18n (en/fr) - Test suite and verification scripts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
13 KiB
Story 5.1: Admin Login
Status: done
Story
En tant qu'Admin, Je veux me connecter à un dashboard admin sécurisé, de sorte que je puisse gérer le système et les utilisateurs.
Acceptance Criteria
- Admin Login Page: Page
/admin/loginavec formulaire de connexion par mot de passe unique - Secure Authentication: Authentification via POST
/api/v1/admin/loginavec mot de passe admin - Token Storage: Stockage du token admin dans localStorage (clé:
admin_token) - Redirect After Login: Redirection vers
/adminaprès connexion réussie - Error Handling: Affichage d'erreur claire si mot de passe incorrect (401)
- Loading States: Spinner pendant la requête de connexion
- Password Toggle: Bouton pour afficher/masquer le mot de passe
- Non-Admin Rejection: Si user connecté via JWT standard mais non admin → 403 Forbidden
- Session Persistence: Token persiste entre les navigations (localStorage)
- Admin Route Protection: Middleware côté client pour protéger les routes
/admin/* - Colocation: Tous les fichiers dans
frontend/src/app/admin/login/
Tasks / Subtasks
-
Task 1: Créer la structure de dossiers (AC: #11)
- 1.1 Créer
frontend/src/app/admin/login/page.tsx(fichier spécial: minuscules!) - 1.2 Créer
frontend/src/app/admin/login/types.ts - 1.3 Créer
frontend/src/app/admin/login/useAdminLogin.ts
- 1.1 Créer
-
Task 2: Définir les types TypeScript (AC: #2)
- 2.1
AdminLoginRequest:{ password: string } - 2.2
AdminLoginResponse:{ status: string, access_token: string, token_type: string, expires_in: number, message: string }
- 2.1
-
Task 3: Créer useAdminLogin.ts hook (AC: #2, #3, #4, #5, #6)
- 3.1
login(password): POST/api/v1/admin/login - 3.2 Stocker
access_tokendans localStorage sous cléadmin_token - 3.3 Gestion erreurs: 401 → "Mot de passe incorrect", timeout → message spécifique
- 3.4 Loading state:
isLoading - 3.5 Redirection vers
/adminaprès succès
- 3.1
-
Task 4: Créer la page de login (AC: #1, #5, #6, #7)
- 4.1 Layout centré avec gradient background (réutiliser style existant)
- 4.2 Formulaire avec input password et bouton show/hide
- 4.3 Icône Shield pour branding admin
- 4.4 Bouton submit avec loading spinner
- 4.5 Affichage erreur en rouge avec icône AlertCircle
-
Task 5: Créer middleware de protection admin (AC: #8, #10)
- 5.1 Vérifier présence de
admin_tokendans localStorage - 5.2 Si absent → rediriger vers
/admin/login?redirect=/admin/... - 5.3 Optionnel: Vérifier validité du token via GET
/api/v1/admin/verify
- 5.1 Vérifier présence de
-
Task 6: Intégration avec layout admin existant (AC: #4, #10)
- 6.1 Modifier
frontend/src/app/admin/layout.tsxpour ajouter protection - 6.2 Vérifier que le layout admin existant fonctionne avec auth
- 6.1 Modifier
-
Task 7: Tests et validation (AC: Tous)
- 7.1
npm run build→ 0 erreurs TypeScript - 7.2 Tester connexion avec bon mot de passe → redirection OK
- 7.3 Tester connexion avec mauvais mot de passe → erreur affichée
- 7.4 Tester accès direct à
/adminsans token → redirection login - 7.5 Tester persistance du token après refresh page
- 7.1
Dev Notes
🏗️ Stack Technique
| Technologie | Version |
|---|---|
| Next.js | 16.0.6 (App Router) |
| React | 19.2.0 |
| Tailwind CSS | configuré |
| shadcn/ui | Card, Button, Input (optionnel) |
| Lucide React | Shield, Lock, Eye, EyeOff, AlertCircle |
📁 Structure Cible (Colocation Pattern)
frontend/src/app/admin/
├── layout.tsx # Layout admin avec protection (existe déjà)
├── page.tsx # Dashboard admin principal (existe déjà)
└── login/
├── page.tsx # Page de connexion admin
├── types.ts # TypeScript interfaces
└── useAdminLogin.ts # Hook pour login
⚠️ Règle absolue (architecture.md):
🚨 FICHIERS SPÉCIAUX: page.tsx, layout.tsx → TOUJOURS minuscules
🚨 COLOCATION: Components/hooks/types dans le dossier de leur page
🔗 API Endpoint Backend (Déjà implémenté)
| Endpoint | Méthode | Request | Response |
|---|---|---|---|
/api/v1/admin/login |
POST | {password: string} |
200: {status, access_token, token_type, expires_in, message} |
/api/v1/admin/verify |
GET | Header: Authorization: Bearer <token> |
200: {status: "valid", authenticated: true} |
/api/v1/admin/logout |
POST | Header: Authorization: Bearer <token> |
200: {status: "success", message: "Logged out"} |
Codes erreur:
- 401:
Invalid credentials→ mot de passe incorrect - 503:
Admin authentication not configured→ ADMIN_PASSWORD non configuré
📊 API Response Example
POST /api/v1/admin/login (200):
{
"status": "success",
"access_token": "abc123...xyz789",
"token_type": "bearer",
"expires_in": 86400,
"message": "Login successful"
}
POST /api/v1/admin/login (401):
{
"detail": "Invalid credentials"
}
🎨 Patterns UI à Réutiliser
Depuis frontend/src/app/(app)/admin/login/page.tsx (existant mais dans mauvais dossier):
- Layout gradient:
bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 - Card avec
bg-black/30 backdrop-blur-xl rounded-2xl border border-white/10 - Input avec icône Lock à gauche, Eye/EyeOff à droite
- Button loading avec spinner
- Error display avec AlertCircle icon
⚠️ IMPORTANT: Ce fichier existe déjà dans frontend/src/app/(app)/admin/login/page.tsx. Il faut le DÉPLACER vers frontend/src/app/admin/login/page.tsx pour respecter la structure colocation.
🔄 State Flow
┌─────────────────────────────────────────────────────────┐
│ /admin/login │
│ ┌─────────────────────────────────────────────────────┐│
│ │ useAdminLogin() hook ││
│ │ ├─ login(password): Promise<void> ││
│ │ ├─ isLoading: boolean ││
│ │ └─ error: string | null ││
│ └─────────────────────────────────────────────────────┘│
│ │
│ Form submit → login(password) │
│ ├─ Success → localStorage.setItem('admin_token', token)│
│ │ → router.push('/admin') ││
│ └─ Error → set error message │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ /admin/* (protected) │
│ ┌─────────────────────────────────────────────────────┐│
│ │ AdminLayout ││
│ │ ├─ Check localStorage.getItem('admin_token') ││
│ │ ├─ If null → redirect to /admin/login ││
│ │ └─ If present → render children ││
│ └─────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘
⚠️ Points d'Attention Critiques
-
Dossier existant: Un fichier
frontend/src/app/(app)/admin/login/page.tsxexiste déjà. Il faut le MIGRER versfrontend/src/app/admin/login/page.tsxpour respecter la structure sans route group(app). -
Token Storage: Utiliser la clé
admin_token(pastokenqui est pour les users normaux). Le store ZustanduseTranslationStorea déjà une méthodesetAdminToken(). -
Double Auth System:
- Users normaux: JWT via
/api/v1/auth/login→ stocké danstoken - Admin: Bearer token via
/api/v1/admin/login→ stocké dansadmin_token - Ces deux systèmes sont INDÉPENDANTS
- Users normaux: JWT via
-
Middleware Protection: Le layout admin doit vérifier le token avant de rendre les enfants. Si pas de token → redirect vers login.
-
Environment Variables: Le backend nécessite
ADMIN_PASSWORDouADMIN_PASSWORD_HASHpour fonctionner. Si non configuré, le backend retourne 503. -
Suspense Boundary: Le composant utilise
useSearchParams→ doit être wrappé dans<Suspense>.
📋 Checklist de Validation Avant Dev
- Backend API
/api/v1/admin/loginest fonctionnel - Variable d'environnement
ADMIN_PASSWORDconfigurée dans.env - Zustand store
useTranslationStoreasetAdminToken()disponible - Le fichier
frontend/src/app/(app)/admin/login/page.tsxexiste (à migrer) - Layout admin
frontend/src/app/admin/layout.tsxexiste
🚀 Migration Steps
L'implémentation existe partiellement dans frontend/src/app/(app)/admin/login/page.tsx. Voici les étapes de migration:
- Déplacer le fichier vers
frontend/src/app/admin/login/page.tsx - Extraire le hook dans
useAdminLogin.ts - Extraire les types dans
types.ts - Ajouter protection dans le layout admin pour redirect si pas authentifié
References
- [Source: _bmad-output/planning-artifacts/epics.md#Story-5.1] — Story requirements
- [Source: _bmad-output/planning-artifacts/architecture.md#Frontend-Architecture] — Colocation pattern
- [Source: _bmad-output/planning-artifacts/architecture.md#API-Response-Formats] — Format réponse API
- [Source: routes/admin_routes.py#L140-154] — Backend login implementation
- [Source: frontend/src/app/(app)/admin/login/page.tsx] — Implémentation existante à migrer
- [Source: frontend/src/lib/store.ts] — Zustand store avec setAdminToken()
- [Source: office-translator-landing-page/app/admin/layout.tsx] — Layout admin à adapter
Dev Agent Record
Agent Model Used
zai-anthropic/glm-5
Debug Log References
None
Completion Notes List
-
2026-02-24: Completed implementation of Admin Login feature
- Created types.ts with AdminLoginRequest, AdminLoginResponse, AdminLoginState interfaces
- Created useAdminLogin.ts hook with login function, error handling, loading states
- Created login page with gradient background, password toggle, error display, loading spinner
- Created admin layout.tsx with route protection (redirects to login if no token)
- Created admin page.tsx that redirects to login (placeholder for future dashboard)
- Removed old admin files from (app) route group to avoid route conflicts
- Build passes with 0 TypeScript errors
-
2026-02-24: Code Review Fixes Applied
- [HIGH] Security: Replaced SHA256 with bcrypt for admin password hashing in routes/admin_routes.py
- [HIGH] Security: Added server-side token verification via /api/v1/admin/verify in AdminLayout
- [MEDIUM]: Updated admin page.tsx to show dashboard placeholder with logout button
- [MEDIUM]: Added logout function to useAdminLogin hook that invalidates server token
- [LOW]: Removed API_BASE URL exposure in error messages for security
File List
Created files:
- frontend/src/app/admin/login/page.tsx
- frontend/src/app/admin/login/types.ts
- frontend/src/app/admin/login/useAdminLogin.ts
- frontend/src/app/admin/layout.tsx
- frontend/src/app/admin/page.tsx
Deleted files:
- frontend/src/app/(app)/admin/login/page.tsx
- frontend/src/app/(app)/admin/page.tsx
Change Log
- 2026-02-24: Story created - ready for development
- 2026-02-24: Implementation complete - all ACs satisfied, ready for review
- 2026-02-24: Code review complete - 2 HIGH, 3 MEDIUM, 1 LOW issues fixed
Senior Developer Review (AI)
Reviewer: AI Code Review (zai-anthropic/glm-5) Date: 2026-02-24
Issues Found & Fixed
| Severity | Issue | File | Status |
|---|---|---|---|
| HIGH | SHA256 password hashing (should use bcrypt) | routes/admin_routes.py:53-54 | ✅ Fixed |
| HIGH | No server-side token verification in AdminLayout | frontend/src/app/admin/layout.tsx | ✅ Fixed |
| MEDIUM | Admin page redirects instead of showing dashboard | frontend/src/app/admin/page.tsx | ✅ Fixed |
| MEDIUM | No logout function to invalidate server token | useAdminLogin.ts | ✅ Fixed |
| LOW | API_BASE URL exposed in error messages | useAdminLogin.ts:40,55 | ✅ Fixed |
Review Outcome
APPROVED - All issues fixed. Story marked as done.