Checkpoint: Migration complète vers l'authentification locale avec email/mot de passe

 Modifications apportées :
- Remplacement de l'authentification OAuth Manus par un système local
- Ajout des champs email, password, emailVerified dans la table users
- Implémentation du hashage des mots de passe avec bcrypt
- Création des procédures auth.login et auth.register
- Mise à jour du système de context pour utiliser JWT
- Création des pages de connexion et d'inscription
- Mise à jour de la page AdminUsers pour créer des utilisateurs avec mot de passe
- Ajout de la protection des routes d'administration (vérification du rôle admin)

 Tests effectués :
- Connexion avec email/mot de passe ✓
- Création d'utilisateurs via l'interface d'administration ✓
- Protection des routes (seuls les admins peuvent accéder au dashboard) ✓
- Redirection automatique après connexion ✓

Note : Les fichiers OAuth Manus originaux ont été conservés pour référence mais ne sont plus utilisés.
This commit is contained in:
Manus Sandbox
2025-11-19 05:35:17 -05:00
parent 934edc63cd
commit cc0bd97248
16 changed files with 212 additions and 20 deletions

View File

@@ -97,6 +97,33 @@ export default function DashboardLayout({
);
}
// Vérifier que l'utilisateur est administrateur
if (user.role !== 'admin') {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="flex flex-col items-center gap-8 p-8 max-w-md w-full">
<div className="flex flex-col items-center gap-6">
<div className="text-center space-y-2">
<h1 className="text-2xl font-bold tracking-tight text-destructive">Accès refusé</h1>
<p className="text-sm text-muted-foreground">
Vous n'avez pas les permissions nécessaires pour accéder à cette page.
</p>
</div>
</div>
<Button
onClick={() => {
window.location.href = '/';
}}
size="lg"
className="w-full shadow-lg hover:shadow-xl transition-all"
>
Retour à l'accueil
</Button>
</div>
</div>
);
}
return (
<SidebarProvider
style={

View File

@@ -41,9 +41,9 @@ export default function AdminUsers() {
const [resetPasswordDialogOpen, setResetPasswordDialogOpen] = useState(false);
const [selectedUser, setSelectedUser] = useState<any>(null);
const [createForm, setCreateForm] = useState({
openId: "",
name: "",
email: "",
password: "",
role: "user" as "user" | "admin",
});
const [editForm, setEditForm] = useState({
@@ -60,9 +60,9 @@ export default function AdminUsers() {
refetch();
setCreateDialogOpen(false);
setCreateForm({
openId: "",
name: "",
email: "",
password: "",
role: "user",
});
},
@@ -118,15 +118,19 @@ export default function AdminUsers() {
};
const handleSaveCreate = () => {
if (!createForm.openId) {
toast.error("L'identifiant OpenID est obligatoire");
if (!createForm.email) {
toast.error("L'email est obligatoire");
return;
}
if (!createForm.password) {
toast.error("Le mot de passe est obligatoire");
return;
}
createMutation.mutate({
openId: createForm.openId,
email: createForm.email,
password: createForm.password,
name: createForm.name || undefined,
email: createForm.email || undefined,
role: createForm.role,
isActive: true,
});
@@ -373,12 +377,13 @@ export default function AdminUsers() {
<div className="space-y-4">
<div>
<Label htmlFor="createOpenId">Identifiant OpenID *</Label>
<Label htmlFor="createPassword">Mot de passe *</Label>
<Input
id="createOpenId"
value={createForm.openId}
onChange={(e) => setCreateForm({ ...createForm, openId: e.target.value })}
placeholder="Identifiant unique de l'utilisateur"
id="createPassword"
type="password"
value={createForm.password}
onChange={(e) => setCreateForm({ ...createForm, password: e.target.value })}
placeholder="Mot de passe de l'utilisateur"
/>
</div>