cleanup session, idp api

This commit is contained in:
peintnermax
2024-08-30 09:24:48 +02:00
parent 5d8807905c
commit 03230de33e
9 changed files with 4854 additions and 6102 deletions

View File

@@ -1,29 +0,0 @@
import { startIdentityProviderFlow } from "@/lib/zitadel";
import { NextRequest, NextResponse } from "next/server";
import { toJson } from "@zitadel/client";
import { StartIdentityProviderIntentResponseSchema } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
export async function POST(request: NextRequest) {
const body = await request.json();
if (body) {
let { idpId, successUrl, failureUrl } = body;
return startIdentityProviderFlow({
idpId,
urls: {
successUrl,
failureUrl,
},
})
.then((resp) => {
return NextResponse.json(
toJson(StartIdentityProviderIntentResponseSchema, resp),
);
})
.catch((error) => {
return NextResponse.json(error, { status: 500 });
});
} else {
return NextResponse.json({}, { status: 400 });
}
}

View File

@@ -1,169 +0,0 @@
import { idpTypeToSlug } from "@/lib/idp";
import {
getActiveIdentityProviders,
getLoginSettings,
getOrgsByDomain,
listAuthenticationMethodTypes,
listUsers,
startIdentityProviderFlow,
} from "@/lib/zitadel";
import { createSessionForUserIdAndUpdateCookie } from "@/utils/session";
import { NextRequest, NextResponse } from "next/server";
const ORG_SUFFIX_REGEX = /(?<=@)(.+)/;
export async function POST(request: NextRequest) {
const body = await request.json();
if (body) {
const { loginName, authRequestId, organization } = body;
return listUsers({
userName: loginName,
organizationId: organization,
}).then(async (users) => {
if (users.details?.totalResult == BigInt(1) && users.result[0].userId) {
const userId = users.result[0].userId;
return createSessionForUserIdAndUpdateCookie(
userId,
undefined,
undefined,
authRequestId,
)
.then((session) => {
if (session.factors?.user?.id) {
return listAuthenticationMethodTypes(session.factors?.user?.id)
.then((methods) => {
return NextResponse.json({
authMethodTypes: methods.authMethodTypes,
sessionId: session.id,
factors: session.factors,
});
})
.catch((error) => {
return NextResponse.json(error, { status: 500 });
});
} else {
throw { details: "No user id found in session" };
}
})
.catch((error) => {
console.error(error);
return NextResponse.json(error, { status: 500 });
});
} else {
const loginSettings = await getLoginSettings(organization);
// 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
if (
loginSettings?.allowRegister &&
!loginSettings?.allowUsernamePassword
) {
// TODO redirect to loginname page with idp hint
const identityProviders = await getActiveIdentityProviders(
organization,
).then((resp) => {
return resp.identityProviders;
});
if (identityProviders.length === 1) {
const host = request.nextUrl.origin;
const identityProviderType = identityProviders[0].type;
const provider = idpTypeToSlug(identityProviderType);
const params = new URLSearchParams();
if (authRequestId) {
params.set("authRequestId", authRequestId);
}
if (organization) {
params.set("organization", organization);
}
return startIdentityProviderFlow({
idpId: identityProviders[0].id,
urls: {
successUrl:
`${host}/idp/${provider}/success?` +
new URLSearchParams(params),
failureUrl:
`${host}/idp/${provider}/failure?` +
new URLSearchParams(params),
},
}).then((resp: any) => {
if (resp.authUrl) {
return NextResponse.json({ nextStep: resp.authUrl });
}
});
} else {
return NextResponse.json(
{ message: "Could not find user" },
{ status: 404 },
);
}
} else if (
loginSettings?.allowRegister &&
loginSettings?.allowUsernamePassword
) {
let orgToRegisterOn: string | undefined = organization;
if (
!orgToRegisterOn &&
loginName &&
ORG_SUFFIX_REGEX.test(loginName)
) {
const matched = ORG_SUFFIX_REGEX.exec(loginName);
const suffix = matched?.[1] ?? "";
// this just returns orgs where the suffix is set as primary domain
const orgs = await getOrgsByDomain(suffix);
const orgToCheckForDiscovery =
orgs.result && orgs.result.length === 1
? orgs.result[0].id
: undefined;
const orgLoginSettings = await getLoginSettings(
orgToCheckForDiscovery,
);
if (orgLoginSettings?.allowDomainDiscovery) {
orgToRegisterOn = orgToCheckForDiscovery;
}
}
const params: any = {};
if (authRequestId) {
params.authRequestId = authRequestId;
}
if (loginName) {
params.email = loginName;
}
if (orgToRegisterOn) {
params.organization = orgToRegisterOn;
}
const registerUrl = new URL(
"/register?" + new URLSearchParams(params),
request.url,
);
return NextResponse.json({
nextStep: registerUrl,
status: 200,
});
}
return NextResponse.json(
{ message: "Could not find user" },
{ status: 404 },
);
}
});
} else {
return NextResponse.error();
}
}

View File

@@ -0,0 +1,20 @@
"use server";
import { startIdentityProviderFlow } from "@/lib/zitadel";
export type StartIDPFlowOptions = {
idpId: string;
successUrl: string;
failureUrl: string;
};
export async function startIDPFlow(options: StartIDPFlowOptions) {
const { idpId, successUrl, failureUrl } = options;
return startIdentityProviderFlow({
idpId,
urls: {
successUrl,
failureUrl,
},
});
}

View File

@@ -0,0 +1,172 @@
"use server";
import {
deleteSession,
getSession,
getUserByID,
listAuthenticationMethodTypes,
} from "@/lib/zitadel";
import {
getMostRecentSessionCookie,
getSessionCookieById,
getSessionCookieByLoginName,
removeSessionFromCookie,
} from "@zitadel/next";
import {
createSessionAndUpdateCookie,
createSessionForIdpAndUpdateCookie,
setSessionAndUpdateCookie,
} from "@/utils/session";
import { headers } from "next/headers";
import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
import { RequestChallenges } from "@zitadel/proto/zitadel/session/v2/challenge_pb";
type CreateNewSessionOptinos = {
userId: string;
idpIntent: {
idpIntentId: string;
idpIntentType: string;
};
loginName: string;
password: string;
organization: string;
authRequestId: string;
};
export async function createNewSession(options: CreateNewSessionOptinos) {
const {
userId,
idpIntent,
loginName,
password,
organization,
authRequestId,
} = options;
if (userId && idpIntent) {
return createSessionForIdpAndUpdateCookie(
userId,
idpIntent,
organization,
authRequestId,
);
} else {
return createSessionAndUpdateCookie(
loginName,
password,
undefined,
organization,
authRequestId,
);
}
}
export type UpdateSessionOptions = {
loginName?: string;
sessionId?: string;
organization?: string;
checks: Checks;
authRequestId?: string;
challenges?: RequestChallenges;
};
export async function updateSession(options: UpdateSessionOptions) {
const {
loginName,
sessionId,
organization,
checks,
authRequestId,
challenges,
} = options;
const sessionPromise = sessionId
? getSessionCookieById({ sessionId }).catch((error) => {
return Promise.reject(error);
})
: loginName
? getSessionCookieByLoginName({ loginName, organization }).catch(
(error) => {
return Promise.reject(error);
},
)
: getMostRecentSessionCookie().catch((error) => {
return Promise.reject(error);
});
const host = headers().get("host");
if (
host &&
challenges &&
challenges.webAuthN &&
!challenges.webAuthN.domain
) {
challenges.webAuthN.domain = host;
}
const recent = await sessionPromise;
if (recent && challenges && (!challenges.otpEmail || !challenges.otpSms)) {
const sessionResponse = await getSession(recent.id, recent.token);
if (sessionResponse && sessionResponse.session.factors.user.id) {
const userResponse = await getUserByID(
sessionResponse.session.factors.user.id,
);
const humanUser =
userResponse.user.type.case === "human"
? userResponse.user.type.value
: undefined;
if (!challenges.otpEmail && humanUser.email.email) {
challenges.otpEmail = humanUser.email.email;
}
if (!challenges.otpSms && humanUser.phone.phone) {
challenges.otpSms = humanUser.phone.phone;
}
}
}
const session = await setSessionAndUpdateCookie(
recent,
checks,
challenges,
authRequestId,
);
// if password, check if user has MFA methods
let authMethods;
if (checks && checks.password && session.factors.user.id) {
const response = await listAuthenticationMethodTypes(
session.factors.user.id,
);
if (response.authMethodTypes && response.authMethodTypes.length) {
authMethods = response.authMethodTypes;
}
}
return {
sessionId: session.id,
factors: session.factors,
challenges: session.challenges,
authMethods,
};
}
type ClearSessionOptions = {
sessionId: string;
};
export async function clearSession(options: ClearSessionOptions) {
const { sessionId } = options;
const session = await getSessionCookieById({ sessionId });
const deletedSession = await deleteSession(session.id, session.token);
if (deletedSession) {
return removeSessionFromCookie(session);
}
}

View File

@@ -9,8 +9,12 @@ import { Spinner } from "./Spinner";
import Alert from "./Alert";
import BackButton from "./BackButton";
import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
import {
CheckPassword,
Checks,
} from "@zitadel/proto/zitadel/session/v2/session_service_pb";
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
import { updateSession } from "@/lib/server/session";
type Inputs = {
password: string;
@@ -47,28 +51,19 @@ export default function PasswordForm({
setError("");
setLoading(true);
const res = await fetch("/api/session", {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
loginName,
organization,
checks: {
password: { password: values.password },
} as Checks,
authRequestId,
}),
const response = await updateSession({
loginName,
organization,
checks: {
password: { password: values.password },
} as Checks,
authRequestId,
}).catch((error) => {
setError(error ?? "Could not verify password");
});
const response = await res.json();
setLoading(false);
if (!res.ok) {
setError(response.details?.details ?? "Could not verify password");
return Promise.reject(response.details);
}
return response;
}
@@ -99,123 +94,129 @@ export default function PasswordForm({
return response;
}
function submitPasswordAndContinue(value: Inputs): Promise<boolean | void> {
return submitPassword(value).then((resp) => {
// if user has mfa -> /otp/[method] or /u2f
// if mfa is forced and user has no mfa -> /mfa/set
// if no passwordless -> /passkey/add
async function submitPasswordAndContinue(
value: Inputs,
): Promise<boolean | void> {
const submitted = await submitPassword(value);
// if user has mfa -> /otp/[method] or /u2f
// if mfa is forced and user has no mfa -> /mfa/set
// if no passwordless -> /passkey/add
// exclude password and passwordless
const availableSecondFactors = resp.authMethods?.filter(
(m: AuthenticationMethodType) =>
m !== AuthenticationMethodType.PASSWORD &&
m !== AuthenticationMethodType.PASSKEY,
// exclude password and passwordless
if (!submitted || !submitted.authMethods) {
setError("Could not verify password");
return;
}
const availableSecondFactors = submitted?.authMethods?.filter(
(m: AuthenticationMethodType) =>
m !== AuthenticationMethodType.PASSWORD &&
m !== AuthenticationMethodType.PASSKEY,
);
if (availableSecondFactors.length == 1) {
const params = new URLSearchParams({
loginName: submitted.factors.user.loginName,
});
if (authRequestId) {
params.append("authRequestId", authRequestId);
}
if (organization) {
params.append("organization", organization);
}
const factor = availableSecondFactors[0];
// if passwordless is other method, but user selected password as alternative, perform a login
if (factor === AuthenticationMethodType.TOTP) {
return router.push(`/otp/time-based?` + params);
} else if (factor === AuthenticationMethodType.OTP_SMS) {
return router.push(`/otp/sms?` + params);
} else if (factor === AuthenticationMethodType.OTP_EMAIL) {
return router.push(`/otp/email?` + params);
} else if (factor === AuthenticationMethodType.U2F) {
return router.push(`/u2f?` + params);
}
} else if (availableSecondFactors.length >= 1) {
const params = new URLSearchParams({
loginName: submitted.factors.user.loginName,
});
if (authRequestId) {
params.append("authRequestId", authRequestId);
}
if (organization) {
params.append("organization", organization);
}
return router.push(`/mfa?` + params);
} else if (
submitted.factors &&
!submitted.factors.passwordless && // if session was not verified with a passkey
promptPasswordless && // if explicitly prompted due policy
!isAlternative // escaped if password was used as an alternative method
) {
const params = new URLSearchParams({
loginName: submitted.factors.user.loginName,
promptPasswordless: "true",
});
if (authRequestId) {
params.append("authRequestId", authRequestId);
}
if (organization) {
params.append("organization", organization);
}
return router.push(`/passkey/add?` + params);
} else if (loginSettings?.forceMfa && !availableSecondFactors.length) {
const params = new URLSearchParams({
loginName: submitted.factors.user.loginName,
checkAfter: "true", // this defines if the check is directly made after the setup
});
if (authRequestId) {
params.append("authRequestId", authRequestId);
}
if (organization) {
params.append("organization", organization);
}
return router.push(`/mfa/set?` + params);
} else if (authRequestId && submitted.sessionId) {
const params = new URLSearchParams({
sessionId: submitted.sessionId,
authRequest: authRequestId,
});
if (organization) {
params.append("organization", organization);
}
return router.push(`/login?` + params);
} else {
// without OIDC flow
const params = new URLSearchParams(
authRequestId
? {
loginName: submitted.factors.user.loginName,
authRequestId,
}
: {
loginName: submitted.factors.user.loginName,
},
);
if (availableSecondFactors.length == 1) {
const params = new URLSearchParams({
loginName: resp.factors.user.loginName,
});
if (authRequestId) {
params.append("authRequestId", authRequestId);
}
if (organization) {
params.append("organization", organization);
}
const factor = availableSecondFactors[0];
// if passwordless is other method, but user selected password as alternative, perform a login
if (factor === AuthenticationMethodType.TOTP) {
return router.push(`/otp/time-based?` + params);
} else if (factor === AuthenticationMethodType.OTP_SMS) {
return router.push(`/otp/sms?` + params);
} else if (factor === AuthenticationMethodType.OTP_EMAIL) {
return router.push(`/otp/email?` + params);
} else if (factor === AuthenticationMethodType.U2F) {
return router.push(`/u2f?` + params);
}
} else if (availableSecondFactors.length >= 1) {
const params = new URLSearchParams({
loginName: resp.factors.user.loginName,
});
if (authRequestId) {
params.append("authRequestId", authRequestId);
}
if (organization) {
params.append("organization", organization);
}
return router.push(`/mfa?` + params);
} else if (
resp.factors &&
!resp.factors.passwordless && // if session was not verified with a passkey
promptPasswordless && // if explicitly prompted due policy
!isAlternative // escaped if password was used as an alternative method
) {
const params = new URLSearchParams({
loginName: resp.factors.user.loginName,
promptPasswordless: "true",
});
if (authRequestId) {
params.append("authRequestId", authRequestId);
}
if (organization) {
params.append("organization", organization);
}
return router.push(`/passkey/add?` + params);
} else if (loginSettings?.forceMfa && !availableSecondFactors.length) {
const params = new URLSearchParams({
loginName: resp.factors.user.loginName,
checkAfter: "true", // this defines if the check is directly made after the setup
});
if (authRequestId) {
params.append("authRequestId", authRequestId);
}
if (organization) {
params.append("organization", organization);
}
return router.push(`/mfa/set?` + params);
} else if (authRequestId && resp.sessionId) {
const params = new URLSearchParams({
sessionId: resp.sessionId,
authRequest: authRequestId,
});
if (organization) {
params.append("organization", organization);
}
return router.push(`/login?` + params);
} else {
// without OIDC flow
const params = new URLSearchParams(
authRequestId
? {
loginName: resp.factors.user.loginName,
authRequestId,
}
: {
loginName: resp.factors.user.loginName,
},
);
if (organization) {
params.append("organization", organization);
}
return router.push(`/signedin?` + params);
if (organization) {
params.append("organization", organization);
}
});
return router.push(`/signedin?` + params);
}
}
return (

View File

@@ -12,6 +12,7 @@ import Alert from "./Alert";
import { IdentityProvider } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
import { idpTypeToSlug } from "@/lib/idp";
import { IdentityProviderType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
import { startIDPFlow } from "@/lib/server/idp";
export interface SignInWithIDPProps {
children?: ReactNode;
@@ -44,27 +45,18 @@ export function SignInWithIDP({
params.set("organization", organization);
}
const res = await fetch("/api/idp/start", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
idpId,
successUrl:
`${host}/idp/${provider}/success?` + new URLSearchParams(params),
failureUrl:
`${host}/idp/${provider}/failure?` + new URLSearchParams(params),
}),
const response = await startIDPFlow({
idpId,
successUrl:
`${host}/idp/${provider}/success?` + new URLSearchParams(params),
failureUrl:
`${host}/idp/${provider}/failure?` + new URLSearchParams(params),
}).catch((err) => {
setError(response.details);
});
const response = await res.json();
setLoading(false);
if (!res.ok) {
setError(response.details);
return Promise.reject(response.details);
}
return response;
}

View File

@@ -12,6 +12,8 @@ import {
PasskeysType,
} from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
import BackButton from "./BackButton";
import { sendLoginname, SendLoginnameOptions } from "@/lib/server/loginname";
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
type Inputs = {
loginName: string;
@@ -51,161 +53,142 @@ export default function UsernameForm({
async function submitLoginName(values: Inputs, organization?: string) {
setLoading(true);
let body: any = {
const options: SendLoginnameOptions = {
loginName: values.loginName,
organization,
authRequestId,
};
if (organization) {
body.organization = organization;
}
if (authRequestId) {
body.authRequestId = authRequestId;
}
const res = await fetch("/api/loginname", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
const res = await sendLoginname(options).catch((error) => {
setError(error ?? "An internal error occurred");
return Promise.reject(error ?? "An internal error occurred");
});
setLoading(false);
if (!res.ok) {
const response = await res.json();
setError(response.message ?? "An internal error occurred");
return Promise.reject(response.message ?? "An internal error occurred");
}
return res.json();
return res;
}
function setLoginNameAndGetAuthMethods(
async function setLoginNameAndGetAuthMethods(
values: Inputs,
organization?: string,
) {
return submitLoginName(values, organization).then((response) => {
if (response.nextStep) {
return router.push(response.nextStep);
} else if (response.authMethodTypes.length == 1) {
const method = response.authMethodTypes[0];
switch (method) {
case 1: // user has only password as auth method
const paramsPassword: any = {
loginName: response.factors.user.loginName,
};
const response = await submitLoginName(values, organization);
// TODO: does this have to be checked in loginSettings.allowDomainDiscovery
if (response?.authMethodTypes && response.authMethodTypes.length === 0) {
setError(
"User has no available authentication methods. Contact your administrator to setup authentication for the requested user.",
);
}
if (organization || response.factors.user.organizationId) {
paramsPassword.organization =
organization ?? response.factors.user.organizationId;
}
if (
loginSettings?.passkeysType &&
(loginSettings?.passkeysType === PasskeysType.ALLOWED ||
(loginSettings.passkeysType as string) ===
"PASSKEYS_TYPE_ALLOWED")
) {
paramsPassword.promptPasswordless = `true`;
}
if (authRequestId) {
paramsPassword.authRequestId = authRequestId;
}
return router.push(
"/password?" + new URLSearchParams(paramsPassword),
);
case 2: // 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 if (
response.authMethodTypes &&
response.authMethodTypes.length === 0
) {
setError(
"User has no available authentication methods. Contact your administrator to setup authentication for the requested user.",
);
} 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 (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,
};
if (authRequestId) {
passkeyParams.authRequestId = authRequestId;
// TODO: does this have to be checked in loginSettings.allowDomainDiscovery
if (organization || response?.factors?.user?.organizationId) {
paramsPassword.organization =
organization ?? response?.factors?.user?.organizationId;
}
if (organization || response.factors.user.organizationId) {
passkeyParams.organization =
organization ?? response.factors.user.organizationId;
if (
loginSettings?.passkeysType &&
(loginSettings?.passkeysType === PasskeysType.ALLOWED ||
(loginSettings.passkeysType as string) ===
"PASSKEYS_TYPE_ALLOWED")
) {
paramsPassword.promptPasswordless = `true`;
}
if (authRequestId) {
paramsPassword.authRequestId = authRequestId;
}
return router.push(
"/passkey/login?" + new URLSearchParams(passkeyParams),
"/password?" + new URLSearchParams(paramsPassword),
);
} else {
// user has no passkey setup and login settings allow passkeys
const paramsPasswordDefault: any = { loginName: values.loginName };
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) {
paramsPasswordDefault.promptPasswordless = `true`; // PasskeysType.PASSKEYS_TYPE_ALLOWED,
paramsPasskeyDefault.promptPasswordless = `true`; // PasskeysType.PASSKEYS_TYPE_ALLOWED,
}
if (authRequestId) {
paramsPasswordDefault.authRequestId = authRequestId;
paramsPasskeyDefault.authRequestId = authRequestId;
}
if (organization || response.factors.user.organizationId) {
paramsPasswordDefault.organization =
organization ?? response.factors.user.organizationId;
if (organization || response?.factors?.user?.organizationId) {
paramsPasskeyDefault.organization =
organization ?? response?.factors?.user?.organizationId;
}
return router.push(
"/password?" + new URLSearchParams(paramsPasswordDefault),
"/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
};
const { errors } = formState;
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(() => {
if (submit && loginName) {

View File

@@ -3,7 +3,7 @@
"tasks": {
"generate": {
"outputs": ["zitadel/**"],
"cache": true
"cache": false
}
}
}

10046
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff