Merge pull request #499 from zitadel/saml-idp

fix(saml-idp): post binding
This commit is contained in:
Max Peintner
2025-07-01 14:25:32 +02:00
committed by GitHub
5 changed files with 64 additions and 34 deletions

View File

@@ -217,7 +217,7 @@ export async function GET(request: NextRequest) {
params.set("organization", organization);
}
return startIdentityProviderFlow({
let url: string | null = await startIdentityProviderFlow({
serviceUrl,
idpId,
urls: {
@@ -228,14 +228,21 @@ export async function GET(request: NextRequest) {
`${origin}/idp/${provider}/failure?` +
new URLSearchParams(params),
},
}).then((resp) => {
if (
resp.nextStep.value &&
typeof resp.nextStep.value === "string"
) {
return NextResponse.redirect(resp.nextStep.value);
}
});
if (!url) {
return NextResponse.json(
{ error: "Could not start IDP flow" },
{ status: 500 },
);
}
if (url.startsWith("/")) {
// if the url is a relative path, construct the absolute url
url = constructUrl(request, url).toString();
}
return NextResponse.redirect(url);
}
}
}

View File

@@ -16,7 +16,7 @@ export default getRequestConfig(async () => {
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const i18nOrganization = _headers.get("x-zitadel-i18n-organization") || ""; // You may need to set this header in middleware
console.log("i18nOrganization:", i18nOrganization);
let translations: JsonObject | {} = {};
try {
const i18nJSON = await getHostedLoginTranslation({

View File

@@ -74,22 +74,20 @@ export type StartIDPFlowCommand = {
async function startIDPFlow(command: StartIDPFlowCommand) {
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
return startIdentityProviderFlow({
const url = await startIdentityProviderFlow({
serviceUrl: command.serviceUrl,
idpId: command.idpId,
urls: {
successUrl: `${command.host.includes("localhost") ? "http://" : "https://"}${command.host}${basePath}${command.successUrl}`,
failureUrl: `${command.host.includes("localhost") ? "http://" : "https://"}${command.host}${basePath}${command.failureUrl}`,
},
}).then((response) => {
if (
response &&
response.nextStep.case === "authUrl" &&
response?.nextStep.value
) {
return { redirect: response.nextStep.value };
}
});
if (!url) {
return { error: "Could not start IDP flow" };
}
return { redirect: url };
}
type CreateNewSessionCommand = {

View File

@@ -102,7 +102,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
const resp = await startIdentityProviderFlow({
const url = await startIdentityProviderFlow({
serviceUrl,
idpId: identityProviders[0].id,
urls: {
@@ -115,9 +115,11 @@ export async function sendLoginname(command: SendLoginnameCommand) {
},
});
if (resp?.nextStep.case === "authUrl") {
return { redirect: resp.nextStep.value };
if (!url) {
return { error: "Could not start IDP flow" };
}
return { redirect: url };
}
};
@@ -166,7 +168,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
const resp = await startIdentityProviderFlow({
const url = await startIdentityProviderFlow({
serviceUrl,
idpId: idp.id,
urls: {
@@ -179,9 +181,11 @@ export async function sendLoginname(command: SendLoginnameCommand) {
},
});
if (resp?.nextStep.case === "authUrl") {
return { redirect: resp.nextStep.value };
if (!url) {
return { error: "Could not start IDP flow" };
}
return { redirect: url };
}
};

View File

@@ -23,7 +23,10 @@ import {
import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
import { SettingsService } from "@zitadel/proto/zitadel/settings/v2/settings_service_pb";
import { SendEmailVerificationCodeSchema } from "@zitadel/proto/zitadel/user/v2/email_pb";
import type { RedirectURLsJson } from "@zitadel/proto/zitadel/user/v2/idp_pb";
import type {
FormData,
RedirectURLsJson,
} from "@zitadel/proto/zitadel/user/v2/idp_pb";
import {
NotificationType,
SendPasswordResetLinkSchema,
@@ -88,7 +91,6 @@ export async function getHostedLoginTranslation({
{},
)
.then((resp) => {
console.log(resp);
return resp.translations ? resp.translations : undefined;
});
@@ -964,19 +966,38 @@ export async function startIdentityProviderFlow({
serviceUrl: string;
idpId: string;
urls: RedirectURLsJson;
}) {
}): Promise<string | null> {
const userService: Client<typeof UserService> = await createServiceForHost(
UserService,
serviceUrl,
);
return userService.startIdentityProviderIntent({
idpId,
content: {
case: "urls",
value: urls,
},
});
return userService
.startIdentityProviderIntent({
idpId,
content: {
case: "urls",
value: urls,
},
})
.then((resp) => {
if (resp.nextStep.case === "authUrl" && resp.nextStep.value) {
return resp.nextStep.value;
} else if (resp.nextStep.case === "formData" && resp.nextStep.value) {
const formData: FormData = resp.nextStep.value;
const redirectUrl = "/saml-post";
const params = new URLSearchParams({ url: formData.url });
Object.entries(formData.fields).forEach(([k, v]) => {
params.append(k, v);
});
return `${redirectUrl}?${params.toString()}`;
} else {
return null;
}
});
}
export async function startLDAPIdentityProviderFlow({