mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-23 17:55:17 +00:00
login route handler
This commit is contained in:
@@ -100,6 +100,7 @@ export async function GET(request: NextRequest) {
|
|||||||
sessions = await loadSessions({ serviceUrl, ids });
|
sessions = await loadSessions({ serviceUrl, ids });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// complete flow if session and request id are provided
|
||||||
if (requestId && sessionId) {
|
if (requestId && sessionId) {
|
||||||
if (requestId.startsWith("oidc_")) {
|
if (requestId.startsWith("oidc_")) {
|
||||||
// this finishes the login process for OIDC
|
// this finishes the login process for OIDC
|
||||||
@@ -122,206 +123,251 @@ export async function GET(request: NextRequest) {
|
|||||||
request,
|
request,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (requestId && requestId.startsWith("oidc_")) {
|
// continue with OIDC
|
||||||
const { authRequest } = await getAuthRequest({
|
if (requestId && requestId.startsWith("oidc_")) {
|
||||||
serviceUrl,
|
const { authRequest } = await getAuthRequest({
|
||||||
authRequestId: requestId.replace("oidc_", ""),
|
serviceUrl,
|
||||||
});
|
authRequestId: requestId.replace("oidc_", ""),
|
||||||
|
});
|
||||||
|
|
||||||
let organization = "";
|
let organization = "";
|
||||||
let suffix = "";
|
let suffix = "";
|
||||||
let idpId = "";
|
let idpId = "";
|
||||||
|
|
||||||
if (authRequest?.scope) {
|
if (authRequest?.scope) {
|
||||||
const orgScope = authRequest.scope.find((s: string) =>
|
const orgScope = authRequest.scope.find((s: string) =>
|
||||||
ORG_SCOPE_REGEX.test(s),
|
ORG_SCOPE_REGEX.test(s),
|
||||||
|
);
|
||||||
|
|
||||||
|
const idpScope = authRequest.scope.find((s: string) =>
|
||||||
|
IDP_SCOPE_REGEX.test(s),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (orgScope) {
|
||||||
|
const matched = ORG_SCOPE_REGEX.exec(orgScope);
|
||||||
|
organization = matched?.[1] ?? "";
|
||||||
|
} else {
|
||||||
|
const orgDomainScope = authRequest.scope.find((s: string) =>
|
||||||
|
ORG_DOMAIN_SCOPE_REGEX.test(s),
|
||||||
);
|
);
|
||||||
|
|
||||||
const idpScope = authRequest.scope.find((s: string) =>
|
if (orgDomainScope) {
|
||||||
IDP_SCOPE_REGEX.test(s),
|
const matched = ORG_DOMAIN_SCOPE_REGEX.exec(orgDomainScope);
|
||||||
);
|
const orgDomain = matched?.[1] ?? "";
|
||||||
|
if (orgDomain) {
|
||||||
|
const orgs = await getOrgsByDomain({
|
||||||
|
serviceUrl,
|
||||||
|
|
||||||
if (orgScope) {
|
domain: orgDomain,
|
||||||
const matched = ORG_SCOPE_REGEX.exec(orgScope);
|
});
|
||||||
organization = matched?.[1] ?? "";
|
if (orgs.result && orgs.result.length === 1) {
|
||||||
} else {
|
organization = orgs.result[0].id ?? "";
|
||||||
const orgDomainScope = authRequest.scope.find((s: string) =>
|
suffix = orgDomain;
|
||||||
ORG_DOMAIN_SCOPE_REGEX.test(s),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (orgDomainScope) {
|
|
||||||
const matched = ORG_DOMAIN_SCOPE_REGEX.exec(orgDomainScope);
|
|
||||||
const orgDomain = matched?.[1] ?? "";
|
|
||||||
if (orgDomain) {
|
|
||||||
const orgs = await getOrgsByDomain({
|
|
||||||
serviceUrl,
|
|
||||||
|
|
||||||
domain: orgDomain,
|
|
||||||
});
|
|
||||||
if (orgs.result && orgs.result.length === 1) {
|
|
||||||
organization = orgs.result[0].id ?? "";
|
|
||||||
suffix = orgDomain;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (idpScope) {
|
if (idpScope) {
|
||||||
const matched = IDP_SCOPE_REGEX.exec(idpScope);
|
const matched = IDP_SCOPE_REGEX.exec(idpScope);
|
||||||
idpId = matched?.[1] ?? "";
|
idpId = matched?.[1] ?? "";
|
||||||
|
|
||||||
const identityProviders = await getActiveIdentityProviders({
|
const identityProviders = await getActiveIdentityProviders({
|
||||||
|
serviceUrl,
|
||||||
|
orgId: organization ? organization : undefined,
|
||||||
|
}).then((resp) => {
|
||||||
|
return resp.identityProviders;
|
||||||
|
});
|
||||||
|
|
||||||
|
const idp = identityProviders.find((idp) => idp.id === idpId);
|
||||||
|
|
||||||
|
if (idp) {
|
||||||
|
const origin = request.nextUrl.origin;
|
||||||
|
|
||||||
|
const identityProviderType = identityProviders[0].type;
|
||||||
|
let provider = idpTypeToSlug(identityProviderType);
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
if (requestId) {
|
||||||
|
params.set("requestId", requestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (organization) {
|
||||||
|
params.set("organization", organization);
|
||||||
|
}
|
||||||
|
|
||||||
|
return startIdentityProviderFlow({
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
orgId: organization ? organization : undefined,
|
idpId,
|
||||||
|
urls: {
|
||||||
|
successUrl:
|
||||||
|
`${origin}/idp/${provider}/success?` +
|
||||||
|
new URLSearchParams(params),
|
||||||
|
failureUrl:
|
||||||
|
`${origin}/idp/${provider}/failure?` +
|
||||||
|
new URLSearchParams(params),
|
||||||
|
},
|
||||||
}).then((resp) => {
|
}).then((resp) => {
|
||||||
return resp.identityProviders;
|
if (
|
||||||
});
|
resp.nextStep.value &&
|
||||||
|
typeof resp.nextStep.value === "string"
|
||||||
const idp = identityProviders.find((idp) => idp.id === idpId);
|
) {
|
||||||
|
return NextResponse.redirect(resp.nextStep.value);
|
||||||
if (idp) {
|
|
||||||
const origin = request.nextUrl.origin;
|
|
||||||
|
|
||||||
const identityProviderType = identityProviders[0].type;
|
|
||||||
let provider = idpTypeToSlug(identityProviderType);
|
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
|
||||||
|
|
||||||
if (requestId) {
|
|
||||||
params.set("requestId", requestId);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authRequest && authRequest.prompt.includes(Prompt.CREATE)) {
|
||||||
|
const registerUrl = new URL("/register", request.url);
|
||||||
|
if (authRequest.id) {
|
||||||
|
registerUrl.searchParams.set("requestId", `oidc_${authRequest.id}`);
|
||||||
|
}
|
||||||
|
if (organization) {
|
||||||
|
registerUrl.searchParams.set("organization", organization);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.redirect(registerUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// use existing session and hydrate it for oidc
|
||||||
|
if (authRequest && sessions.length) {
|
||||||
|
// if some accounts are available for selection and select_account is set
|
||||||
|
if (authRequest.prompt.includes(Prompt.SELECT_ACCOUNT)) {
|
||||||
|
return gotoAccounts({
|
||||||
|
request,
|
||||||
|
requestId: `oidc_${authRequest.id}`,
|
||||||
|
organization,
|
||||||
|
});
|
||||||
|
} else if (authRequest.prompt.includes(Prompt.LOGIN)) {
|
||||||
|
/**
|
||||||
|
* The login prompt instructs the authentication server to prompt the user for re-authentication, regardless of whether the user is already authenticated
|
||||||
|
*/
|
||||||
|
|
||||||
|
// if a hint is provided, skip loginname page and jump to the next page
|
||||||
|
if (authRequest.loginHint) {
|
||||||
|
try {
|
||||||
|
let command: SendLoginnameCommand = {
|
||||||
|
loginName: authRequest.loginHint,
|
||||||
|
requestId: authRequest.id,
|
||||||
|
};
|
||||||
|
|
||||||
if (organization) {
|
if (organization) {
|
||||||
params.set("organization", organization);
|
command = { ...command, organization };
|
||||||
}
|
}
|
||||||
|
|
||||||
return startIdentityProviderFlow({
|
const res = await sendLoginname(command);
|
||||||
serviceUrl,
|
|
||||||
idpId,
|
if (res && "redirect" in res && res?.redirect) {
|
||||||
urls: {
|
const absoluteUrl = new URL(res.redirect, request.url);
|
||||||
successUrl:
|
return NextResponse.redirect(absoluteUrl.toString());
|
||||||
`${origin}/idp/${provider}/success?` +
|
}
|
||||||
new URLSearchParams(params),
|
} catch (error) {
|
||||||
failureUrl:
|
console.error("Failed to execute sendLoginname:", error);
|
||||||
`${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 (authRequest && authRequest.prompt.includes(Prompt.CREATE)) {
|
const loginNameUrl = new URL("/loginname", request.url);
|
||||||
const registerUrl = new URL("/register", request.url);
|
|
||||||
if (authRequest.id) {
|
if (authRequest.id) {
|
||||||
registerUrl.searchParams.set("requestId", `oidc_${authRequest.id}`);
|
loginNameUrl.searchParams.set("requestId", `oidc_${authRequest.id}`);
|
||||||
|
}
|
||||||
|
if (authRequest.loginHint) {
|
||||||
|
loginNameUrl.searchParams.set("loginName", authRequest.loginHint);
|
||||||
}
|
}
|
||||||
if (organization) {
|
if (organization) {
|
||||||
registerUrl.searchParams.set("organization", organization);
|
loginNameUrl.searchParams.set("organization", organization);
|
||||||
|
}
|
||||||
|
if (suffix) {
|
||||||
|
loginNameUrl.searchParams.set("suffix", suffix);
|
||||||
|
}
|
||||||
|
return NextResponse.redirect(loginNameUrl);
|
||||||
|
} else if (authRequest.prompt.includes(Prompt.NONE)) {
|
||||||
|
/**
|
||||||
|
* With an OIDC none prompt, the authentication server must not display any authentication or consent user interface pages.
|
||||||
|
* This means that the user should not be prompted to enter their password again.
|
||||||
|
* Instead, the server attempts to silently authenticate the user using an existing session or other authentication mechanisms that do not require user interaction
|
||||||
|
**/
|
||||||
|
const selectedSession = await findValidSession({
|
||||||
|
serviceUrl,
|
||||||
|
sessions,
|
||||||
|
authRequest,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!selectedSession || !selectedSession.id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "No active session found" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NextResponse.redirect(registerUrl);
|
const cookie = sessionCookies.find(
|
||||||
}
|
(cookie) => cookie.id === selectedSession.id,
|
||||||
|
);
|
||||||
|
|
||||||
// use existing session and hydrate it for oidc
|
if (!cookie || !cookie.id || !cookie.token) {
|
||||||
if (authRequest && sessions.length) {
|
return NextResponse.json(
|
||||||
// if some accounts are available for selection and select_account is set
|
{ error: "No active session found" },
|
||||||
if (authRequest.prompt.includes(Prompt.SELECT_ACCOUNT)) {
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const session = {
|
||||||
|
sessionId: cookie.id,
|
||||||
|
sessionToken: cookie.token,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { callbackUrl } = await createCallback({
|
||||||
|
serviceUrl,
|
||||||
|
req: create(CreateCallbackRequestSchema, {
|
||||||
|
authRequestId: requestId.replace("oidc_", ""),
|
||||||
|
callbackKind: {
|
||||||
|
case: "session",
|
||||||
|
value: create(SessionSchema, session),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
return NextResponse.redirect(callbackUrl);
|
||||||
|
} else {
|
||||||
|
// check for loginHint, userId hint and valid sessions
|
||||||
|
let selectedSession = await findValidSession({
|
||||||
|
serviceUrl,
|
||||||
|
sessions,
|
||||||
|
authRequest,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!selectedSession || !selectedSession.id) {
|
||||||
return gotoAccounts({
|
return gotoAccounts({
|
||||||
request,
|
request,
|
||||||
requestId: `oidc_${authRequest.id}`,
|
requestId: `oidc_${authRequest.id}`,
|
||||||
organization,
|
organization,
|
||||||
});
|
});
|
||||||
} else if (authRequest.prompt.includes(Prompt.LOGIN)) {
|
}
|
||||||
/**
|
|
||||||
* The login prompt instructs the authentication server to prompt the user for re-authentication, regardless of whether the user is already authenticated
|
|
||||||
*/
|
|
||||||
|
|
||||||
// if a hint is provided, skip loginname page and jump to the next page
|
const cookie = sessionCookies.find(
|
||||||
if (authRequest.loginHint) {
|
(cookie) => cookie.id === selectedSession.id,
|
||||||
try {
|
);
|
||||||
let command: SendLoginnameCommand = {
|
|
||||||
loginName: authRequest.loginHint,
|
|
||||||
requestId: authRequest.id,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (organization) {
|
if (!cookie || !cookie.id || !cookie.token) {
|
||||||
command = { ...command, organization };
|
return gotoAccounts({
|
||||||
}
|
request,
|
||||||
|
requestId: `oidc_${authRequest.id}`,
|
||||||
const res = await sendLoginname(command);
|
organization,
|
||||||
|
|
||||||
if (res && "redirect" in res && res?.redirect) {
|
|
||||||
const absoluteUrl = new URL(res.redirect, request.url);
|
|
||||||
return NextResponse.redirect(absoluteUrl.toString());
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to execute sendLoginname:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const loginNameUrl = new URL("/loginname", request.url);
|
|
||||||
if (authRequest.id) {
|
|
||||||
loginNameUrl.searchParams.set(
|
|
||||||
"requestId",
|
|
||||||
`oidc_${authRequest.id}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (authRequest.loginHint) {
|
|
||||||
loginNameUrl.searchParams.set("loginName", authRequest.loginHint);
|
|
||||||
}
|
|
||||||
if (organization) {
|
|
||||||
loginNameUrl.searchParams.set("organization", organization);
|
|
||||||
}
|
|
||||||
if (suffix) {
|
|
||||||
loginNameUrl.searchParams.set("suffix", suffix);
|
|
||||||
}
|
|
||||||
return NextResponse.redirect(loginNameUrl);
|
|
||||||
} else if (authRequest.prompt.includes(Prompt.NONE)) {
|
|
||||||
/**
|
|
||||||
* With an OIDC none prompt, the authentication server must not display any authentication or consent user interface pages.
|
|
||||||
* This means that the user should not be prompted to enter their password again.
|
|
||||||
* Instead, the server attempts to silently authenticate the user using an existing session or other authentication mechanisms that do not require user interaction
|
|
||||||
**/
|
|
||||||
const selectedSession = await findValidSession({
|
|
||||||
serviceUrl,
|
|
||||||
sessions,
|
|
||||||
authRequest,
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!selectedSession || !selectedSession.id) {
|
const session = {
|
||||||
return NextResponse.json(
|
sessionId: cookie.id,
|
||||||
{ error: "No active session found" },
|
sessionToken: cookie.token,
|
||||||
{ status: 400 },
|
};
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const cookie = sessionCookies.find(
|
|
||||||
(cookie) => cookie.id === selectedSession.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!cookie || !cookie.id || !cookie.token) {
|
|
||||||
return NextResponse.json(
|
|
||||||
{ error: "No active session found" },
|
|
||||||
{ status: 400 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const session = {
|
|
||||||
sessionId: cookie.id,
|
|
||||||
sessionToken: cookie.token,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
try {
|
||||||
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: {
|
||||||
@@ -330,201 +376,155 @@ export async function GET(request: NextRequest) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
return NextResponse.redirect(callbackUrl);
|
if (callbackUrl) {
|
||||||
} else {
|
return NextResponse.redirect(callbackUrl);
|
||||||
// check for loginHint, userId hint and valid sessions
|
|
||||||
let selectedSession = await findValidSession({
|
|
||||||
serviceUrl,
|
|
||||||
sessions,
|
|
||||||
authRequest,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!selectedSession || !selectedSession.id) {
|
|
||||||
return gotoAccounts({
|
|
||||||
request,
|
|
||||||
requestId: `oidc_${authRequest.id}`,
|
|
||||||
organization,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const cookie = sessionCookies.find(
|
|
||||||
(cookie) => cookie.id === selectedSession.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!cookie || !cookie.id || !cookie.token) {
|
|
||||||
return gotoAccounts({
|
|
||||||
request,
|
|
||||||
requestId: `oidc_${authRequest.id}`,
|
|
||||||
organization,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const session = {
|
|
||||||
sessionId: cookie.id,
|
|
||||||
sessionToken: cookie.token,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { callbackUrl } = await createCallback({
|
|
||||||
serviceUrl,
|
|
||||||
|
|
||||||
req: create(CreateCallbackRequestSchema, {
|
|
||||||
authRequestId: requestId.replace("oidc_", ""),
|
|
||||||
callbackKind: {
|
|
||||||
case: "session",
|
|
||||||
value: create(SessionSchema, session),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
if (callbackUrl) {
|
|
||||||
return NextResponse.redirect(callbackUrl);
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
"could not create callback, redirect user to choose other account",
|
|
||||||
);
|
|
||||||
return gotoAccounts({
|
|
||||||
request,
|
|
||||||
organization,
|
|
||||||
requestId: `oidc_${authRequest.id}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
return gotoAccounts({
|
|
||||||
request,
|
|
||||||
requestId: `oidc_${authRequest.id}`,
|
|
||||||
organization,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const loginNameUrl = new URL("/loginname", request.url);
|
|
||||||
|
|
||||||
loginNameUrl.searchParams.set("requestId", requestId);
|
|
||||||
if (authRequest?.loginHint) {
|
|
||||||
loginNameUrl.searchParams.set("loginName", authRequest.loginHint);
|
|
||||||
loginNameUrl.searchParams.set("submit", "true"); // autosubmit
|
|
||||||
}
|
|
||||||
|
|
||||||
if (organization) {
|
|
||||||
loginNameUrl.searchParams.append("organization", organization);
|
|
||||||
// loginNameUrl.searchParams.set("organization", organization);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.redirect(loginNameUrl);
|
|
||||||
}
|
|
||||||
} else if (requestId && requestId.startsWith("saml_")) {
|
|
||||||
// handle saml request
|
|
||||||
const { samlRequest } = await getSAMLRequest({
|
|
||||||
serviceUrl,
|
|
||||||
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 {
|
} else {
|
||||||
return NextResponse.json(
|
console.log(
|
||||||
{ error: "Failed to complete SAML request" },
|
"could not create callback, redirect user to choose other account",
|
||||||
{ status: response.status },
|
|
||||||
);
|
);
|
||||||
|
return gotoAccounts({
|
||||||
|
request,
|
||||||
|
organization,
|
||||||
|
requestId: `oidc_${authRequest.id}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
console.log(
|
console.error(error);
|
||||||
"could not create response, redirect user to choose other account",
|
|
||||||
);
|
|
||||||
return gotoAccounts({
|
return gotoAccounts({
|
||||||
request,
|
request,
|
||||||
requestId: `saml_${samlRequest.id}`,
|
requestId: `oidc_${authRequest.id}`,
|
||||||
|
organization,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}
|
||||||
console.error(error);
|
} else {
|
||||||
|
const loginNameUrl = new URL("/loginname", request.url);
|
||||||
|
|
||||||
|
loginNameUrl.searchParams.set("requestId", requestId);
|
||||||
|
if (authRequest?.loginHint) {
|
||||||
|
loginNameUrl.searchParams.set("loginName", authRequest.loginHint);
|
||||||
|
loginNameUrl.searchParams.set("submit", "true"); // autosubmit
|
||||||
|
}
|
||||||
|
|
||||||
|
if (organization) {
|
||||||
|
loginNameUrl.searchParams.append("organization", organization);
|
||||||
|
// loginNameUrl.searchParams.set("organization", organization);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.redirect(loginNameUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// continue with SAML
|
||||||
|
else if (requestId && requestId.startsWith("saml_")) {
|
||||||
|
const { samlRequest } = await getSAMLRequest({
|
||||||
|
serviceUrl,
|
||||||
|
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({
|
return gotoAccounts({
|
||||||
request,
|
request,
|
||||||
requestId: `saml_${samlRequest.id}`,
|
requestId: `saml_${samlRequest.id}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
return NextResponse.json(
|
console.error(error);
|
||||||
{ error: "No authRequest nor samlRequest provided" },
|
return gotoAccounts({
|
||||||
{ status: 500 },
|
request,
|
||||||
);
|
requestId: `saml_${samlRequest.id}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "No authRequest nor samlRequest provided" },
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user