implement redirects from server

This commit is contained in:
peintnermax
2024-09-11 15:27:31 +02:00
parent 8cc0d487ae
commit 39bb4c2994
2 changed files with 99 additions and 128 deletions

View File

@@ -1,5 +1,7 @@
"use server"; "use server";
import { PasskeysType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
import { headers } from "next/headers"; import { headers } from "next/headers";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { createSessionForUserIdAndUpdateCookie } from "../../utils/session"; import { createSessionForUserIdAndUpdateCookie } from "../../utils/session";
@@ -24,6 +26,8 @@ export async function sendLoginname(command: SendLoginnameCommand) {
organizationId: command.organization, organizationId: command.organization,
}); });
const loginSettings = await getLoginSettings(command.organization);
if (users.details?.totalResult == BigInt(1) && users.result[0].userId) { if (users.details?.totalResult == BigInt(1) && users.result[0].userId) {
const userId = users.result[0].userId; const userId = users.result[0].userId;
const session = await createSessionForUserIdAndUpdateCookie( const session = await createSessionForUserIdAndUpdateCookie(
@@ -41,14 +45,102 @@ export async function sendLoginname(command: SendLoginnameCommand) {
session.factors?.user?.id, session.factors?.user?.id,
); );
return { if (!methods.authMethodTypes || !methods.authMethodTypes.length) {
authMethodTypes: methods.authMethodTypes, throw Error(
sessionId: session.id, "User has no available authentication methods. Contact your administrator to setup authentication for the requested user.",
factors: session.factors, );
}; }
if (methods.authMethodTypes.length == 1) {
const method = methods.authMethodTypes[0];
switch (method) {
case AuthenticationMethodType.PASSWORD: // user has only password as auth method
const paramsPassword: any = {
loginName: session.factors?.user?.loginName,
};
// TODO: does this have to be checked in loginSettings.allowDomainDiscovery
if (command.organization || session.factors?.user?.organizationId) {
paramsPassword.organization =
command.organization ?? session.factors?.user?.organizationId;
}
if (
loginSettings?.passkeysType &&
loginSettings?.passkeysType === PasskeysType.ALLOWED
) {
paramsPassword.promptPasswordless = `true`;
}
if (command.authRequestId) {
paramsPassword.authRequestId = command.authRequestId;
}
return redirect("/password?" + new URLSearchParams(paramsPassword));
case AuthenticationMethodType.PASSKEY: // AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY
const paramsPasskey: any = { loginName: command.loginName };
if (command.authRequestId) {
paramsPasskey.authRequestId = command.authRequestId;
}
if (command.organization || session.factors?.user?.organizationId) {
paramsPasskey.organization =
command.organization ?? session.factors?.user?.organizationId;
}
return redirect(
"/passkey/login?" + new URLSearchParams(paramsPasskey),
);
}
} else {
// prefer passkey in favor of other methods
if (methods.authMethodTypes.includes(AuthenticationMethodType.PASSKEY)) {
const passkeyParams: any = {
loginName: command.loginName,
altPassword: `${methods.authMethodTypes.includes(1)}`, // show alternative password option
};
if (command.authRequestId) {
passkeyParams.authRequestId = command.authRequestId;
}
if (command.organization || session.factors?.user?.organizationId) {
passkeyParams.organization =
command.organization ?? session.factors?.user?.organizationId;
}
return redirect("/passkey/login?" + new URLSearchParams(passkeyParams));
} else if (
methods.authMethodTypes.includes(AuthenticationMethodType.IDP)
) {
// TODO: redirect user to idp
} else if (
methods.authMethodTypes.includes(AuthenticationMethodType.PASSWORD)
) {
// user has no passkey setup and login settings allow passkeys
const paramsPasswordDefault: any = { loginName: command.loginName };
if (loginSettings?.passkeysType === 1) {
paramsPasswordDefault.promptPasswordless = `true`; // PasskeysType.PASSKEYS_TYPE_ALLOWED,
}
if (command.authRequestId) {
paramsPasswordDefault.authRequestId = command.authRequestId;
}
if (command.organization || session.factors?.user?.organizationId) {
paramsPasswordDefault.organization =
command.organization ?? session.factors?.user?.organizationId;
}
return redirect(
"/password?" + new URLSearchParams(paramsPasswordDefault),
);
}
}
} }
const loginSettings = await getLoginSettings(command.organization);
// TODO: check if allowDomainDiscovery has to be allowed too, to redirect to the register page // TODO: check if allowDomainDiscovery has to be allowed too, to redirect to the register page
// user not found, check if register is enabled on organization // user not found, check if register is enabled on organization

View File

@@ -1,11 +1,7 @@
"use client"; "use client";
import { sendLoginname } from "@/lib/server/loginname"; import { sendLoginname } from "@/lib/server/loginname";
import { import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
LoginSettings,
PasskeysType,
} from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { ReactNode, useEffect, useState } from "react"; import { ReactNode, useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
@@ -71,123 +67,6 @@ export default function UsernameForm({
organization?: string, organization?: string,
) { ) {
const response = await submitLoginName(values, organization); const response = await submitLoginName(values, organization);
if (!response) {
return;
}
if (response.authMethodTypes && response.authMethodTypes.length === 0) {
setError(
"User has no available authentication methods. Contact your administrator to setup authentication for the requested user.",
);
return;
}
if (response.authMethodTypes.length == 1) {
const method = response.authMethodTypes[0];
switch (method) {
case AuthenticationMethodType.PASSWORD: // user has only password as auth method
const paramsPassword: any = {
loginName: response.factors?.user?.loginName,
};
// TODO: does this have to be checked in loginSettings.allowDomainDiscovery
if (organization || response.factors?.user?.organizationId) {
paramsPassword.organization =
organization ?? response.factors?.user?.organizationId;
}
if (
loginSettings?.passkeysType &&
loginSettings?.passkeysType === PasskeysType.ALLOWED
) {
paramsPassword.promptPasswordless = `true`;
}
if (authRequestId) {
paramsPassword.authRequestId = authRequestId;
}
return router.push(
"/password?" + new URLSearchParams(paramsPassword),
);
case AuthenticationMethodType.PASSKEY: // AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY
const paramsPasskey: any = { loginName: values.loginName };
if (authRequestId) {
paramsPasskey.authRequestId = authRequestId;
}
if (organization || response.factors?.user?.organizationId) {
paramsPasskey.organization =
organization ?? response.factors?.user?.organizationId;
}
return router.push(
"/passkey/login?" + new URLSearchParams(paramsPasskey),
);
default:
const paramsPasskeyDefault: any = { loginName: values.loginName };
if (loginSettings?.passkeysType === 1) {
paramsPasskeyDefault.promptPasswordless = `true`; // PasskeysType.PASSKEYS_TYPE_ALLOWED,
}
if (authRequestId) {
paramsPasskeyDefault.authRequestId = authRequestId;
}
if (organization || response.factors?.user?.organizationId) {
paramsPasskeyDefault.organization =
organization ?? response.factors?.user?.organizationId;
}
return router.push(
"/password?" + new URLSearchParams(paramsPasskeyDefault),
);
}
} else {
// prefer passkey in favor of other methods
if (response.authMethodTypes.includes(2)) {
const passkeyParams: any = {
loginName: values.loginName,
altPassword: `${response.authMethodTypes.includes(1)}`, // show alternative password option
};
if (authRequestId) {
passkeyParams.authRequestId = authRequestId;
}
if (organization || response.factors?.user?.organizationId) {
passkeyParams.organization =
organization ?? response.factors?.user?.organizationId;
}
return router.push(
"/passkey/login?" + new URLSearchParams(passkeyParams),
);
} else {
// user has no passkey setup and login settings allow passkeys
const paramsPasswordDefault: any = { loginName: values.loginName };
if (loginSettings?.passkeysType === 1) {
paramsPasswordDefault.promptPasswordless = `true`; // PasskeysType.PASSKEYS_TYPE_ALLOWED,
}
if (authRequestId) {
paramsPasswordDefault.authRequestId = authRequestId;
}
if (organization || response.factors?.user?.organizationId) {
paramsPasswordDefault.organization =
organization ?? response.factors?.user?.organizationId;
}
return router.push(
"/password?" + new URLSearchParams(paramsPasswordDefault),
);
}
}
} }
useEffect(() => { useEffect(() => {