fix(login): better error handling for saml cookie serialization (#10259)

Fixes issues where SAML identity provider authentication would fail
silently, leaving users unable to complete the login flow through
external SAML providers.

changes `saml.ts`:

- Enhanced
[setSAMLFormCookie()](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)
with proper error handling and logging
- Improved
[getSAMLFormCookie()](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)
with detailed error reporting
- Added cookie size validation and warnings

changes `zitadel.ts`:

- Enhanced
[startIdentityProviderFlow()](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)
with robust form data handling
- Added detailed logging for protobuf object structure analysis
- Implemented safe fallback serialization for complex objects
- Added comprehensive error handling for JSON operations
This commit is contained in:
Max Peintner 2025-07-15 09:46:18 +02:00 committed by GitHub
parent d5d6d37a25
commit 14a5946db8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 119 additions and 16 deletions

View File

@ -25,29 +25,70 @@ export async function getSAMLFormUID() {
export async function setSAMLFormCookie(value: string): Promise<string> {
const cookiesList = await cookies();
const uid = await getSAMLFormUID();
await cookiesList.set({
name: uid,
value: value,
httpOnly: true,
path: "/",
maxAge: 5 * 60, // 5 minutes
});
try {
// Check cookie size limits (typical limit is 4KB)
if (value.length > 4000) {
console.warn(
`SAML form cookie value is large (${value.length} characters), may exceed browser limits`,
);
}
return uid;
// Log the attempt
console.log(
`Setting SAML form cookie with uid: ${uid}, value length: ${value.length}`,
);
await cookiesList.set({
name: uid,
value: value,
httpOnly: true,
path: "/",
maxAge: 5 * 60, // 5 minutes
});
// Note: We can't reliably verify immediately due to Next.js cookies API behavior
// Instead, we'll rely on the getSAMLFormCookie function to detect failures
console.log(`Successfully set SAML form cookie with uid: ${uid}`);
return uid;
} catch (error) {
console.error(`Failed to set SAML form cookie with uid: ${uid}`, {
error,
valueLength: value.length,
uid,
});
throw new Error(
`Failed to set SAML form cookie: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
export async function getSAMLFormCookie(uid: string): Promise<string | null> {
const cookiesList = await cookies();
const cookie = cookiesList.get(uid);
if (!cookie || !cookie.value) {
try {
const cookie = cookiesList.get(uid);
if (!cookie) {
console.warn(`SAML form cookie not found for uid: ${uid}`);
return null;
}
if (!cookie.value) {
console.warn(`SAML form cookie found but empty value for uid: ${uid}`);
return null;
}
console.log(
`Successfully retrieved SAML form cookie for uid: ${uid}, value length: ${cookie.value.length}`,
);
return cookie.value;
} catch (error) {
console.error(`Error retrieving SAML form cookie for uid: ${uid}`, error);
return null;
}
return cookie.value;
}
export async function loginWithSAMLAndSession({

View File

@ -989,10 +989,72 @@ export async function startIdentityProviderFlow({
const formData: FormData = resp.nextStep.value;
const redirectUrl = "/saml-post";
const dataId = await setSAMLFormCookie(JSON.stringify(formData.fields));
const params = new URLSearchParams({ url: formData.url, id: dataId });
try {
// Log the attempt with structure inspection
console.log("Attempting to stringify formData.fields:", {
fields: formData.fields,
fieldsType: typeof formData.fields,
fieldsKeys: Object.keys(formData.fields || {}),
fieldsEntries: Object.entries(formData.fields || {}),
});
return `${redirectUrl}?${params.toString()}`;
const stringifiedFields = JSON.stringify(formData.fields);
console.log(
"Successfully stringified formData.fields, length:",
stringifiedFields.length,
);
// Check cookie size limits (typical limit is 4KB)
if (stringifiedFields.length > 4000) {
console.warn(
`SAML form cookie value is large (${stringifiedFields.length} characters), may exceed browser limits`,
);
}
const dataId = await setSAMLFormCookie(stringifiedFields);
const params = new URLSearchParams({ url: formData.url, id: dataId });
return `${redirectUrl}?${params.toString()}`;
} catch (stringifyError) {
console.error("Failed to stringify formData.fields:", {
error: stringifyError,
formDataFields: formData.fields,
formDataUrl: formData.url,
fieldsType: typeof formData.fields,
fieldsConstructor: formData.fields?.constructor?.name,
});
// Try to create a safe serialization by converting to plain object
try {
const safeFields: Record<string, string> = {};
const fieldsObj = formData.fields || {};
// Convert each field to a string if it's not already
for (const [key, value] of Object.entries(fieldsObj)) {
safeFields[key] =
typeof value === "string" ? value : String(value);
}
console.log(
"Using safe serialization for formData.fields:",
safeFields,
);
const safeStringified = JSON.stringify(safeFields);
const dataId = await setSAMLFormCookie(safeStringified);
const params = new URLSearchParams({
url: formData.url,
id: dataId,
});
return `${redirectUrl}?${params.toString()}`;
} catch (fallbackError) {
console.error("Safe serialization also failed:", fallbackError);
throw new Error(
`Failed to serialize SAML form data: ${stringifyError instanceof Error ? stringifyError.message : String(stringifyError)}`,
);
}
}
} else {
return null;
}