mirror of
https://github.com/zitadel/zitadel.git
synced 2026-08-18 17:05:03 -05:00
fix(login): retry logic for session creation after registration on NotFound (#12189)
After user registration, the backend projections may not be up to date
yet when the Login UI immediately tries to create a session. This
results in a `QUERY-Dfbg2` ("User could not be found") error even though
the user was created successfully.
This adds retry logic with backoff (500ms/1s/2s, up to 3 attempts)
around `createSessionAndUpdateCookie` in the registration flow. Only
`NotFound` errors are retried — other errors are thrown immediately.
Closes #12173
---------
Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Livio Spring
Copilot Autofix powered by AI
parent
f4f43f5248
commit
fc6e2a0f05
@@ -2,17 +2,49 @@
|
||||
|
||||
import { createSessionAndUpdateCookie, createSessionForIdpAndUpdateCookie } from "@/lib/server/cookie";
|
||||
import { addHumanUser, addIDPLink, getLoginSettings, getUserByID, listAuthenticationMethodTypes } from "@/lib/zitadel";
|
||||
import { create } from "@zitadel/client";
|
||||
import { Code, ConnectError, Duration, create } from "@zitadel/client";
|
||||
import { Factors } from "@zitadel/proto/zitadel/session/v2/session_pb";
|
||||
import { ChecksJson, ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
||||
import { Checks, ChecksJson, ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
|
||||
import crypto from "crypto";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { completeFlowOrGetUrl } from "../client";
|
||||
import { getOrSetFingerprintId } from "../fingerprint";
|
||||
import { createLogger } from "../logger";
|
||||
import { getServiceConfig } from "../service-url";
|
||||
import { checkEmailVerification, checkMFAFactors } from "../verify-helper";
|
||||
|
||||
const logger = createLogger("register");
|
||||
|
||||
const MAX_SESSION_RETRIES = 3;
|
||||
const RETRY_DELAYS_MS = [500, 1000, 2000];
|
||||
|
||||
/**
|
||||
* After user creation, backend projections (users, login_names) may not be updated yet.
|
||||
* This helper retries createSessionAndUpdateCookie on NotFound errors with increasing delays.
|
||||
*/
|
||||
async function createSessionWithRetry(command: { checks: Checks; requestId: string | undefined; lifetime?: Duration }) {
|
||||
let lastError: unknown;
|
||||
for (let attempt = 0; attempt < MAX_SESSION_RETRIES; attempt++) {
|
||||
try {
|
||||
return await createSessionAndUpdateCookie(command);
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
const isNotFound = error instanceof ConnectError && error.code === Code.NotFound;
|
||||
const isLastAttempt = attempt + 1 >= MAX_SESSION_RETRIES;
|
||||
if (!isNotFound || isLastAttempt) {
|
||||
throw error;
|
||||
}
|
||||
const delay = RETRY_DELAYS_MS[attempt] ?? 2000;
|
||||
logger.warn(
|
||||
`Session creation failed with NotFound (attempt ${attempt + 1}/${MAX_SESSION_RETRIES}), retrying in ${delay}ms...`,
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
}
|
||||
}
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
type RegisterUserCommand = {
|
||||
email: string;
|
||||
firstName: string;
|
||||
@@ -74,7 +106,7 @@ export async function registerUser(
|
||||
|
||||
const checks = create(ChecksSchema, checkPayload);
|
||||
|
||||
const result = await createSessionAndUpdateCookie({
|
||||
const result = await createSessionWithRetry({
|
||||
checks,
|
||||
requestId: command.requestId,
|
||||
lifetime: command.password ? loginSettings?.passwordCheckLifetime : undefined,
|
||||
|
||||
Reference in New Issue
Block a user