form post

This commit is contained in:
Max Peintner
2025-02-14 16:38:03 +01:00
parent 8bd673dd28
commit ba3c3d596d
2 changed files with 124 additions and 30 deletions

View File

@@ -7,6 +7,7 @@ import { getServiceUrlFromHeaders } from "@/lib/service";
import { findValidSession } from "@/lib/session"; import { findValidSession } from "@/lib/session";
import { import {
createCallback, createCallback,
createResponse,
getActiveIdentityProviders, getActiveIdentityProviders,
getAuthRequest, getAuthRequest,
getOrgsByDomain, getOrgsByDomain,
@@ -15,14 +16,12 @@ import {
startIdentityProviderFlow, startIdentityProviderFlow,
} from "@/lib/zitadel"; } from "@/lib/zitadel";
import { create } from "@zitadel/client"; import { create } from "@zitadel/client";
import { import { Prompt } from "@zitadel/proto/zitadel/oidc/v2/authorization_pb";
AuthRequest,
Prompt,
} from "@zitadel/proto/zitadel/oidc/v2/authorization_pb";
import { import {
CreateCallbackRequestSchema, CreateCallbackRequestSchema,
SessionSchema, SessionSchema,
} from "@zitadel/proto/zitadel/oidc/v2/oidc_service_pb"; } from "@zitadel/proto/zitadel/oidc/v2/oidc_service_pb";
import { CreateResponseRequestSchema } from "@zitadel/proto/zitadel/saml/v2/saml_service_pb";
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb"; import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
import { headers } from "next/headers"; import { headers } from "next/headers";
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
@@ -33,19 +32,17 @@ export const fetchCache = "default-no-store";
const gotoAccounts = ({ const gotoAccounts = ({
request, request,
authRequest, requestId,
organization, organization,
idPrefix,
}: { }: {
request: NextRequest; request: NextRequest;
authRequest: AuthRequest; requestId: string;
organization: string; organization?: string;
idPrefix: string;
}): NextResponse<unknown> => { }): NextResponse<unknown> => {
const accountsUrl = new URL("/accounts", request.url); const accountsUrl = new URL("/accounts", request.url);
if (authRequest?.id) { if (requestId) {
accountsUrl.searchParams.set("requestId", `${idPrefix}${authRequest.id}`); accountsUrl.searchParams.set("requestId", requestId);
} }
if (organization) { if (organization) {
accountsUrl.searchParams.set("organization", organization); accountsUrl.searchParams.set("organization", organization);
@@ -59,7 +56,6 @@ async function loadSessions({
ids, ids,
}: { }: {
serviceUrl: string; serviceUrl: string;
ids: string[]; ids: string[];
}): Promise<Session[]> { }): Promise<Session[]> {
const response = await listSessions({ const response = await listSessions({
@@ -177,7 +173,6 @@ export async function GET(request: NextRequest) {
const identityProviders = await getActiveIdentityProviders({ const identityProviders = await getActiveIdentityProviders({
serviceUrl, serviceUrl,
orgId: organization ? organization : undefined, orgId: organization ? organization : undefined,
}).then((resp) => { }).then((resp) => {
return resp.identityProviders; return resp.identityProviders;
@@ -203,7 +198,6 @@ export async function GET(request: NextRequest) {
return startIdentityProviderFlow({ return startIdentityProviderFlow({
serviceUrl, serviceUrl,
idpId, idpId,
urls: { urls: {
successUrl: successUrl:
@@ -243,9 +237,8 @@ export async function GET(request: NextRequest) {
if (authRequest.prompt.includes(Prompt.SELECT_ACCOUNT)) { if (authRequest.prompt.includes(Prompt.SELECT_ACCOUNT)) {
return gotoAccounts({ return gotoAccounts({
request, request,
authRequest, requestId: `oidc_${authRequest.id}`,
organization, organization,
idPrefix: "oidc_",
}); });
} else if (authRequest.prompt.includes(Prompt.LOGIN)) { } else if (authRequest.prompt.includes(Prompt.LOGIN)) {
/** /**
@@ -300,7 +293,6 @@ export async function GET(request: NextRequest) {
**/ **/
const selectedSession = await findValidSession({ const selectedSession = await findValidSession({
serviceUrl, serviceUrl,
sessions, sessions,
authRequest, authRequest,
}); });
@@ -330,7 +322,6 @@ export async function GET(request: NextRequest) {
const { callbackUrl } = await createCallback({ const { callbackUrl } = await createCallback({
serviceUrl, serviceUrl,
req: create(CreateCallbackRequestSchema, { req: create(CreateCallbackRequestSchema, {
authRequestId: requestId.replace("oidc_", ""), authRequestId: requestId.replace("oidc_", ""),
callbackKind: { callbackKind: {
@@ -351,9 +342,8 @@ export async function GET(request: NextRequest) {
if (!selectedSession || !selectedSession.id) { if (!selectedSession || !selectedSession.id) {
return gotoAccounts({ return gotoAccounts({
request, request,
authRequest, requestId: `oidc_${authRequest.id}`,
organization, organization,
idPrefix: "oidc_",
}); });
} }
@@ -364,9 +354,8 @@ export async function GET(request: NextRequest) {
if (!cookie || !cookie.id || !cookie.token) { if (!cookie || !cookie.id || !cookie.token) {
return gotoAccounts({ return gotoAccounts({
request, request,
authRequest, requestId: `oidc_${authRequest.id}`,
organization, organization,
idPrefix: "oidc_",
}); });
} }
@@ -395,18 +384,16 @@ export async function GET(request: NextRequest) {
); );
return gotoAccounts({ return gotoAccounts({
request, request,
authRequest,
organization, organization,
idPrefix: "oidc_", requestId: `oidc_${authRequest.id}`,
}); });
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return gotoAccounts({ return gotoAccounts({
request, request,
authRequest, requestId: `oidc_${authRequest.id}`,
organization, organization,
idPrefix: "oidc_",
}); });
} }
} }
@@ -432,6 +419,107 @@ export async function GET(request: NextRequest) {
serviceUrl, serviceUrl,
samlRequestId: requestId.replace("saml_", ""), samlRequestId: requestId.replace("saml_", ""),
}); });
if (!samlRequest) {
return NextResponse.json(
{ error: "No samlRequest found" },
{ status: 400 },
);
}
let selectedSession = await findValidSession({
serviceUrl,
sessions,
samlRequest,
});
if (!selectedSession || !selectedSession.id) {
return gotoAccounts({
request,
requestId: `saml_${samlRequest.id}`,
});
}
const cookie = sessionCookies.find(
(cookie) => cookie.id === selectedSession.id,
);
if (!cookie || !cookie.id || !cookie.token) {
return gotoAccounts({
request,
requestId: `saml_${samlRequest.id}`,
// organization,
});
}
const session = {
sessionId: cookie.id,
sessionToken: cookie.token,
};
try {
const { url, binding } = await createResponse({
serviceUrl,
req: create(CreateResponseRequestSchema, {
samlRequestId: requestId.replace("saml_", ""),
responseKind: {
case: "session",
value: session,
},
}),
});
if (url && binding.case === "redirect") {
return NextResponse.redirect(url);
} else if (url && binding.case === "post") {
const formData = {
key1: "value1",
key2: "value2",
};
// Convert form data to URL-encoded string
const formBody = Object.entries(formData)
.map(
([key, value]) =>
encodeURIComponent(key) + "=" + encodeURIComponent(value),
)
.join("&");
// Make a POST request to the external URL with the form data
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: formBody,
});
// Handle the response from the external URL
if (response.ok) {
return NextResponse.json({
message: "SAML request completed successfully",
});
} else {
return NextResponse.json(
{ error: "Failed to complete SAML request" },
{ status: response.status },
);
}
} else {
console.log(
"could not create response, redirect user to choose other account",
);
return gotoAccounts({
request,
requestId: `saml_${samlRequest.id}`,
});
}
} catch (error) {
console.error(error);
return gotoAccounts({
request,
requestId: `saml_${samlRequest.id}`,
});
}
} else { } else {
return NextResponse.json( return NextResponse.json(
{ error: "No authRequest nor samlRequest provided" }, { error: "No authRequest nor samlRequest provided" },

View File

@@ -1,5 +1,6 @@
import { timestampDate } from "@zitadel/client"; import { timestampDate } from "@zitadel/client";
import { AuthRequest } from "@zitadel/proto/zitadel/oidc/v2/authorization_pb"; import { AuthRequest } from "@zitadel/proto/zitadel/oidc/v2/authorization_pb";
import { SAMLRequest } from "@zitadel/proto/zitadel/saml/v2/authorization_pb";
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb"; import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
import { GetSessionResponse } from "@zitadel/proto/zitadel/session/v2/session_service_pb"; import { GetSessionResponse } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb"; import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
@@ -150,21 +151,26 @@ export async function isSessionValid({
export async function findValidSession({ export async function findValidSession({
serviceUrl, serviceUrl,
sessions, sessions,
authRequest, authRequest,
samlRequest,
}: { }: {
serviceUrl: string; serviceUrl: string;
sessions: Session[]; sessions: Session[];
authRequest: AuthRequest; authRequest?: AuthRequest;
samlRequest?: SAMLRequest;
}): Promise<Session | undefined> { }): Promise<Session | undefined> {
const sessionsWithHint = sessions.filter((s) => { const sessionsWithHint = sessions.filter((s) => {
if (authRequest.hintUserId) { if (authRequest && authRequest.hintUserId) {
return s.factors?.user?.id === authRequest.hintUserId; return s.factors?.user?.id === authRequest.hintUserId;
} }
if (authRequest.loginHint) { if (authRequest && authRequest.loginHint) {
return s.factors?.user?.loginName === authRequest.loginHint; return s.factors?.user?.loginName === authRequest.loginHint;
} }
if (samlRequest) {
// TODO: do whatever
return true;
}
return true; return true;
}); });