Parcourir la source

security: validate password reset token on page load

unknown il y a 2 jours
Parent
commit
f4bd8dbd8c
3 fichiers modifiés avec 32 ajouts et 2 suppressions
  1. 7 0
      backend/routers/auth.py
  2. 10 0
      src/lib/api.ts
  3. 15 2
      src/pages/Auth.vue

+ 7 - 0
backend/routers/auth.py

@@ -186,6 +186,13 @@ async def forgot_password(request: schemas.ForgotPassword, lang: str = "en"):
     
     return {"message": "Reset instructions sent to your email"}
 
+@router.get("/verify-reset-token")
+async def verify_reset_token(token: str):
+    user_id = token_service.verify_reset_token(token)
+    if not user_id:
+        raise HTTPException(status_code=400, detail="Invalid or expired reset token")
+    return {"message": "Token is valid"}
+
 @router.post("/reset-password")
 async def reset_password(request: schemas.ResetPassword):
     user_id = token_service.verify_reset_token(request.token)

+ 10 - 0
src/lib/api.ts

@@ -233,6 +233,16 @@ export const resetPassword = async (data: any) => {
   return response.json();
 };
 
+export const verifyResetToken = async (token: string) => {
+  const response = await fetch(`${API_BASE_URL}/auth/verify-reset-token?token=${encodeURIComponent(token)}&lang=${i18n.global.locale.value}`);
+  
+  if (!response.ok) {
+    throw new Error(await getErrorMessage(response, 'Invalid or expired token'));
+  }
+  
+  return response.json();
+};
+
 export const adminGetOrders = async (filters: { search?: string, status?: string, date_from?: string, date_to?: string } = {}) => {
   const token = localStorage.getItem("token");
   const query = new URLSearchParams();

+ 15 - 2
src/pages/Auth.vue

@@ -214,7 +214,7 @@ import { Mail, Lock, ArrowRight, Loader2, ShieldCheck, KeyRound, ArrowLeft, Shie
 import Button from "@/components/ui/button.vue";
 import Logo from "@/components/Logo.vue";
 import LanguageSwitcher from "@/components/LanguageSwitcher.vue";
-import { loginUser, registerUser, forgotPassword, resetPassword, socialLogin } from "@/lib/api";
+import { loginUser, registerUser, forgotPassword, resetPassword, socialLogin, verifyResetToken } from "@/lib/api";
 import { useAuthStore } from "@/stores/auth";
 import i18n, { currentLanguage } from "@/i18n";
 
@@ -247,7 +247,20 @@ onMounted(async () => {
   const verifyToken = route.query.verify_token as string;
   const qMode = route.query.mode as AuthMode;
 
-  if (token) { mode.value = "reset"; formData.token = token; }
+  if (token) {
+    isLoading.value = true;
+    try {
+      await verifyResetToken(token);
+      mode.value = "reset";
+      formData.token = token;
+    } catch (err: any) {
+      toast.error(err.message || "Link has expired");
+      router.replace({ query: { ...route.query, token: undefined } });
+      mode.value = "login";
+    } finally {
+      isLoading.value = false;
+    }
+  }
   if (qMode) { mode.value = qMode; }
 
   // Watch for query changes if already on the page