fix(auth): guard OAuth callback against double token exchange
Publish Docker Image / prepare (push) Successful in 3s
Publish Docker Image / build (linux/amd64, ubuntu-latest) (push) Failing after 9s
Publish Docker Image / build (linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
Publish Docker Image / merge (push) Canceled after 0s
Publish Docker Image / prepare (push) Successful in 3s
Publish Docker Image / build (linux/amd64, ubuntu-latest) (push) Failing after 9s
Publish Docker Image / build (linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
Publish Docker Image / merge (push) Canceled after 0s
This commit is contained in:
@@ -9,6 +9,11 @@ import { Loader2, AlertCircle } from "lucide-react";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
|
|
||||||
|
// Module-level guard so a Suspense/search-params remount of this client
|
||||||
|
// component can't exchange the same OAuth code twice — Keycloak rejects a
|
||||||
|
// reused code with `invalid_grant` ("Code not valid") and the login fails.
|
||||||
|
const processedAuthCodes = new Set<string>();
|
||||||
|
|
||||||
function OAuthCallbackInner() {
|
function OAuthCallbackInner() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -32,6 +37,11 @@ function OAuthCallbackInner() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent a second token exchange for the same code (remount / double
|
||||||
|
// effect). Without this, the second exchange fails with "Code not valid".
|
||||||
|
if (processedAuthCodes.has(code)) return;
|
||||||
|
processedAuthCodes.add(code);
|
||||||
|
|
||||||
// Step-up re-auth for device pairing: the QR generator sent the user here
|
// Step-up re-auth for device pairing: the QR generator sent the user here
|
||||||
// via prompt=login. Don't create a login session — just confirm the fresh
|
// via prompt=login. Don't create a login session — just confirm the fresh
|
||||||
// auth (sets the short-lived pairing proof cookie) and bounce back to the
|
// auth (sets the short-lived pairing proof cookie) and bounce back to the
|
||||||
|
|||||||
Reference in New Issue
Block a user