Parcourir la source

[Auth] Recovery generate: accept Bearer token as auth fallback

Belt-and-suspenders alongside the cookie-name pin: if a client passes
Authorization: Bearer <access_token>, resolve the user via the admin
client instead of the cookie-based server client. Avoids spurious 401s
during the cookie-sync window after signInAnonymously() + immediate
router.push("/recovery").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User il y a 2 mois
Parent
commit
63513494a8
1 fichiers modifiés avec 34 ajouts et 9 suppressions
  1. 34 9
      src/app/api/auth/recovery/generate/route.ts

+ 34 - 9
src/app/api/auth/recovery/generate/route.ts

@@ -1,24 +1,49 @@
-import { NextResponse } from "next/server";
+import { NextRequest, NextResponse } from "next/server";
 import { getSupabaseServerClient } from "@/lib/supabase/server";
 import { getSupabaseAdminClient } from "@/lib/supabase/admin";
 import { generateRecoveryCode, hashRecoveryCode } from "@/lib/auth/recovery";
 
-export async function POST() {
+export async function POST(request: NextRequest) {
   try {
-    const supabase = await getSupabaseServerClient();
-    const {
-      data: { user },
-    } = await supabase.auth.getUser();
+    // Prefer the Authorization: Bearer header (sent explicitly by the client)
+    // because the @supabase/ssr cookie sync after signInAnonymously() can lag
+    // behind the immediate router.push("/recovery") navigation, leaving the
+    // server with no/stale auth cookies and a spurious 401. Falling back to
+    // the cookie-based server client preserves existing behavior for any
+    // caller that doesn't send the header.
+    const authHeader = request.headers.get("authorization");
+    const bearerToken = authHeader?.toLowerCase().startsWith("bearer ")
+      ? authHeader.slice(7).trim()
+      : null;
 
-    if (!user) {
+    const admin = getSupabaseAdminClient();
+    let userId: string | null = null;
+
+    if (bearerToken) {
+      const { data, error } = await admin.auth.getUser(bearerToken);
+      if (!error && data.user) {
+        userId = data.user.id;
+      }
+    }
+
+    if (!userId) {
+      const supabase = await getSupabaseServerClient();
+      const {
+        data: { user },
+      } = await supabase.auth.getUser();
+      if (user) {
+        userId = user.id;
+      }
+    }
+
+    if (!userId) {
       return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
     }
 
     const code = generateRecoveryCode();
     const hashed = await hashRecoveryCode(code);
 
-    const admin = getSupabaseAdminClient();
-    const { error } = await admin.from("users").update({ recovery_code: hashed }).eq("id", user.id);
+    const { error } = await admin.from("users").update({ recovery_code: hashed }).eq("id", userId);
 
     if (error) {
       return NextResponse.json({ error: "Failed to store recovery code" }, { status: 500 });