From 2e5e4f87d509f46e444bb513d6e3cfc0a8d8e5b5 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 26 Feb 2025 09:48:52 +0100 Subject: [PATCH 1/7] implement mfa init prompt --- apps/login/locales/de.json | 3 +- apps/login/locales/en.json | 3 +- apps/login/locales/es.json | 3 +- apps/login/locales/it.json | 3 +- apps/login/locales/zh.json | 3 +- apps/login/src/app/(login)/mfa/set/page.tsx | 6 ++ apps/login/src/lib/server/password.ts | 7 ++- apps/login/src/lib/server/verify.ts | 7 ++- apps/login/src/lib/verify-helper.ts | 63 +++++++++++++-------- 9 files changed, 62 insertions(+), 36 deletions(-) diff --git a/apps/login/locales/de.json b/apps/login/locales/de.json index 5e8756c89b..db46321b05 100644 --- a/apps/login/locales/de.json +++ b/apps/login/locales/de.json @@ -71,7 +71,8 @@ }, "set": { "title": "2-Faktor einrichten", - "description": "Wählen Sie einen der folgenden zweiten Faktoren." + "description": "Wählen Sie einen der folgenden zweiten Faktoren.", + "skip": "Überspringen" } }, "otp": { diff --git a/apps/login/locales/en.json b/apps/login/locales/en.json index 3101f222d5..36776ccbd9 100644 --- a/apps/login/locales/en.json +++ b/apps/login/locales/en.json @@ -71,7 +71,8 @@ }, "set": { "title": "Set up 2-Factor", - "description": "Choose one of the following second factors." + "description": "Choose one of the following second factors.", + "skip": "Skip" } }, "otp": { diff --git a/apps/login/locales/es.json b/apps/login/locales/es.json index 5a9b6f6324..4eba3a9696 100644 --- a/apps/login/locales/es.json +++ b/apps/login/locales/es.json @@ -71,7 +71,8 @@ }, "set": { "title": "Configurar autenticación de 2 factores", - "description": "Elige uno de los siguientes factores secundarios." + "description": "Elige uno de los siguientes factores secundarios.", + "skip": "Omitir" } }, "otp": { diff --git a/apps/login/locales/it.json b/apps/login/locales/it.json index 1423c43cfe..d0969c86b3 100644 --- a/apps/login/locales/it.json +++ b/apps/login/locales/it.json @@ -71,7 +71,8 @@ }, "set": { "title": "Configura l'autenticazione a 2 fattori", - "description": "Scegli uno dei seguenti secondi fattori." + "description": "Scegli uno dei seguenti secondi fattori.", + "skip": "Salta" } }, "otp": { diff --git a/apps/login/locales/zh.json b/apps/login/locales/zh.json index acd03cc5b6..9c87a53a65 100644 --- a/apps/login/locales/zh.json +++ b/apps/login/locales/zh.json @@ -71,7 +71,8 @@ }, "set": { "title": "设置双因素认证", - "description": "选择以下的一个第二因素。" + "description": "选择以下的一个第二因素。", + "skip": "跳过" } }, "otp": { diff --git a/apps/login/src/app/(login)/mfa/set/page.tsx b/apps/login/src/app/(login)/mfa/set/page.tsx index a885a36c7a..a8f16f23f2 100644 --- a/apps/login/src/app/(login)/mfa/set/page.tsx +++ b/apps/login/src/app/(login)/mfa/set/page.tsx @@ -161,6 +161,12 @@ export default async function Page(props: { > )} + {force !== "true" && ( +
+

{t("set.skip")}

+
+ )} +
diff --git a/apps/login/src/lib/server/password.ts b/apps/login/src/lib/server/password.ts index 98c34a3de6..6fd3f67bcd 100644 --- a/apps/login/src/lib/server/password.ts +++ b/apps/login/src/lib/server/password.ts @@ -16,7 +16,7 @@ import { setPassword, setUserPassword, } from "@/lib/zitadel"; -import { create } from "@zitadel/client"; +import { ConnectError, create } from "@zitadel/client"; import { createServerTransport } from "@zitadel/client/node"; import { createUserServiceClient } from "@zitadel/client/v2"; import { @@ -267,7 +267,8 @@ export async function sendPassword(command: UpdateSessionCommand) { return { error: "Could not verify password!" }; } - const mfaFactorCheck = checkMFAFactors( + const mfaFactorCheck = await checkMFAFactors( + serviceUrl, session, loginSettings, authMethods, @@ -433,7 +434,7 @@ export async function checkSessionAndSetPassword({ }, {}, ) - .catch((error) => { + .catch((error: ConnectError) => { console.log(error); if (error.code === 7) { return { error: "Session is not valid." }; diff --git a/apps/login/src/lib/server/verify.ts b/apps/login/src/lib/server/verify.ts index 38bddb9428..4de697e772 100644 --- a/apps/login/src/lib/server/verify.ts +++ b/apps/login/src/lib/server/verify.ts @@ -203,7 +203,8 @@ export async function sendVerification(command: VerifyUserByEmailCommand) { } // redirect to mfa factor if user has one, or redirect to set one up - const mfaFactorCheck = checkMFAFactors( + const mfaFactorCheck = await checkMFAFactors( + serviceUrl, session, loginSettings, authMethodResponse.authMethodTypes, @@ -407,12 +408,12 @@ export async function sendVerificationRedirectWithoutCheck( const loginSettings = await getLoginSettings({ serviceUrl, - organization: user.details?.resourceOwner, }); // redirect to mfa factor if user has one, or redirect to set one up - const mfaFactorCheck = checkMFAFactors( + const mfaFactorCheck = await checkMFAFactors( + serviceUrl, session, loginSettings, authMethodResponse.authMethodTypes, diff --git a/apps/login/src/lib/verify-helper.ts b/apps/login/src/lib/verify-helper.ts index e8afef6890..fcc46e175c 100644 --- a/apps/login/src/lib/verify-helper.ts +++ b/apps/login/src/lib/verify-helper.ts @@ -5,6 +5,7 @@ import { PasswordExpirySettings } from "@zitadel/proto/zitadel/settings/v2/passw import { HumanUser } from "@zitadel/proto/zitadel/user/v2/user_pb"; import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb"; import moment from "moment"; +import { getUserByID } from "./zitadel"; export function checkPasswordChangeRequired( expirySettings: PasswordExpirySettings | undefined, @@ -100,7 +101,8 @@ export function checkEmailVerification( } } -export function checkMFAFactors( +export async function checkMFAFactors( + serviceUrl: string, session: Session, loginSettings: LoginSettings | undefined, authMethods: AuthenticationMethodType[], @@ -188,31 +190,42 @@ export function checkMFAFactors( ); } + // TODO: provide a way to setup passkeys on mfa page? + return { redirect: `/mfa/set?` + params }; + } else if ( + loginSettings?.mfaInitSkipLifetime && + (loginSettings.mfaInitSkipLifetime.nanos > 0 || + loginSettings.mfaInitSkipLifetime.seconds > 0) && + !availableMultiFactors.length && + session?.factors?.user?.id + ) { + const user = await getUserByID({ + serviceUrl, + userId: session.factors?.user?.id, + }); + if ( + user.user?.type?.case === "human" && + user.user?.type?.value.mfaInitSkipped + ) { + } + const params = new URLSearchParams({ + loginName: session.factors?.user?.loginName as string, + force: "false", // this defines if the mfa is not forced in the settings and can be skipped + checkAfter: "true", // this defines if the check is directly made after the setup + }); + + if (requestId) { + params.append("requestId", requestId); + } + + if (organization || session.factors?.user?.organizationId) { + params.append( + "organization", + organization ?? (session.factors?.user?.organizationId as string), + ); + } + // TODO: provide a way to setup passkeys on mfa page? return { redirect: `/mfa/set?` + params }; } - - // TODO: implement passkey setup - - // else if ( - // submitted.factors && - // !submitted.factors.webAuthN && // 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, - // prompt: "true", - // }); - - // if (requestId) { - // params.append("requestId", requestId); - // } - - // if (organization) { - // params.append("organization", organization); - // } - - // return router.push(`/passkey/set?` + params); - // } } From 546edee64ffbc6e06f5346240d04224c43907510 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 26 Feb 2025 16:37:52 +0100 Subject: [PATCH 2/7] fix time check --- apps/login/src/lib/verify-helper.ts | 50 ++++++++++++++++++----------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/apps/login/src/lib/verify-helper.ts b/apps/login/src/lib/verify-helper.ts index fcc46e175c..41a537cde1 100644 --- a/apps/login/src/lib/verify-helper.ts +++ b/apps/login/src/lib/verify-helper.ts @@ -203,29 +203,43 @@ export async function checkMFAFactors( serviceUrl, userId: session.factors?.user?.id, }); + if ( user.user?.type?.case === "human" && user.user?.type?.value.mfaInitSkipped ) { - } - const params = new URLSearchParams({ - loginName: session.factors?.user?.loginName as string, - force: "false", // this defines if the mfa is not forced in the settings and can be skipped - checkAfter: "true", // this defines if the check is directly made after the setup - }); - - if (requestId) { - params.append("requestId", requestId); - } - - if (organization || session.factors?.user?.organizationId) { - params.append( - "organization", - organization ?? (session.factors?.user?.organizationId as string), + const mfaInitSkippedTimestamp = timestampDate( + user.user.type.value.mfaInitSkipped, ); - } - // TODO: provide a way to setup passkeys on mfa page? - return { redirect: `/mfa/set?` + params }; + const mfaInitSkipLifetimeMillis = + Number(loginSettings.mfaInitSkipLifetime.seconds) * 1000 + + loginSettings.mfaInitSkipLifetime.nanos / 1000000; + const currentTime = Date.now(); + const mfaInitSkippedTime = mfaInitSkippedTimestamp.getTime(); + const timeDifference = currentTime - mfaInitSkippedTime; + + if (timeDifference > mfaInitSkipLifetimeMillis) { + const params = new URLSearchParams({ + loginName: session.factors?.user?.loginName as string, + force: "false", // this defines if the mfa is not forced in the settings and can be skipped + checkAfter: "true", // this defines if the check is directly made after the setup + }); + + if (requestId) { + params.append("requestId", requestId); + } + + if (organization || session.factors?.user?.organizationId) { + params.append( + "organization", + organization ?? (session.factors?.user?.organizationId as string), + ); + } + + // TODO: provide a way to setup passkeys on mfa page? + return { redirect: `/mfa/set?` + params }; + } + } } } From 83df30e525a5c8e0c6ca11f8bf178191a84424a8 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Fri, 28 Feb 2025 15:22:56 +0100 Subject: [PATCH 3/7] skip button and server action --- apps/login/src/app/(login)/mfa/set/page.tsx | 29 ++--- .../choose-second-factor-to-setup.tsx | 101 ++++++++++++------ apps/login/src/lib/server/session.ts | 48 +++++++++ apps/login/src/lib/verify-helper.ts | 61 ++++++----- apps/login/src/lib/zitadel.ts | 15 +++ 5 files changed, 173 insertions(+), 81 deletions(-) diff --git a/apps/login/src/app/(login)/mfa/set/page.tsx b/apps/login/src/app/(login)/mfa/set/page.tsx index a8f16f23f2..498b063428 100644 --- a/apps/login/src/app/(login)/mfa/set/page.tsx +++ b/apps/login/src/app/(login)/mfa/set/page.tsx @@ -49,10 +49,10 @@ export default async function Page(props: { const { serviceUrl } = getServiceUrlFromHeaders(_headers); const sessionWithData = sessionId - ? await loadSessionById(serviceUrl, sessionId, organization) - : await loadSessionByLoginname(serviceUrl, loginName, organization); + ? await loadSessionById(sessionId, organization) + : await loadSessionByLoginname(loginName, organization); - async function getAuthMethodsAndUser(host: string, session?: Session) { + async function getAuthMethodsAndUser(session?: Session) { const userId = session?.factors?.user?.id; if (!userId) { @@ -80,7 +80,6 @@ export default async function Page(props: { } async function loadSessionByLoginname( - host: string, loginName?: string, organization?: string, ) { @@ -92,23 +91,18 @@ export default async function Page(props: { organization, }, }).then((session) => { - return getAuthMethodsAndUser(serviceUrl, session); + return getAuthMethodsAndUser(session); }); } - async function loadSessionById( - host: string, - sessionId: string, - organization?: string, - ) { + async function loadSessionById(sessionId: string, organization?: string) { const recent = await getSessionCookieById({ sessionId, organization }); return getSession({ serviceUrl, - sessionId: recent.id, sessionToken: recent.token, }).then((sessionResponse) => { - return getAuthMethodsAndUser(serviceUrl, sessionResponse.session); + return getAuthMethodsAndUser(sessionResponse.session); }); } @@ -147,8 +141,10 @@ export default async function Page(props: { {isSessionValid(sessionWithData).valid && loginSettings && - sessionWithData && ( + sessionWithData && + sessionWithData.factors?.user?.id && ( )} - {force !== "true" && ( -
-

{t("set.skip")}

-
- )} -
diff --git a/apps/login/src/components/choose-second-factor-to-setup.tsx b/apps/login/src/components/choose-second-factor-to-setup.tsx index 21f7aff8a6..e56379e147 100644 --- a/apps/login/src/components/choose-second-factor-to-setup.tsx +++ b/apps/login/src/components/choose-second-factor-to-setup.tsx @@ -1,13 +1,17 @@ "use client"; +import { skipMFAAndContinueWithNextUrl } from "@/lib/server/session"; import { LoginSettings, SecondFactorType, } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb"; import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb"; +import { useTranslations } from "next-intl"; +import { useRouter } from "next/navigation"; import { EMAIL, SMS, TOTP, U2F } from "./auth-methods"; type Props = { + userId: string; loginName?: string; sessionId?: string; requestId?: string; @@ -17,9 +21,11 @@ type Props = { checkAfter: boolean; phoneVerified: boolean; emailVerified: boolean; + force: boolean; }; export function ChooseSecondFactorToSetup({ + userId, loginName, sessionId, requestId, @@ -29,7 +35,10 @@ export function ChooseSecondFactorToSetup({ checkAfter, phoneVerified, emailVerified, + force, }: Props) { + const t = useTranslations("mfa"); + const router = useRouter(); const params = new URLSearchParams({}); if (loginName) { @@ -49,39 +58,63 @@ export function ChooseSecondFactorToSetup({ } return ( -
- {loginSettings.secondFactors.map((factor) => { - switch (factor) { - case SecondFactorType.OTP: - return TOTP( - userMethods.includes(AuthenticationMethodType.TOTP), - "/otp/time-based/set?" + params, - ); - case SecondFactorType.U2F: - return U2F( - userMethods.includes(AuthenticationMethodType.U2F), - "/u2f/set?" + params, - ); - case SecondFactorType.OTP_EMAIL: - return ( - emailVerified && - EMAIL( - userMethods.includes(AuthenticationMethodType.OTP_EMAIL), - "/otp/email/set?" + params, - ) - ); - case SecondFactorType.OTP_SMS: - return ( - phoneVerified && - SMS( - userMethods.includes(AuthenticationMethodType.OTP_SMS), - "/otp/sms/set?" + params, - ) - ); - default: - return null; - } - })} -
+ <> +
+ {loginSettings.secondFactors.map((factor) => { + switch (factor) { + case SecondFactorType.OTP: + return TOTP( + userMethods.includes(AuthenticationMethodType.TOTP), + "/otp/time-based/set?" + params, + ); + case SecondFactorType.U2F: + return U2F( + userMethods.includes(AuthenticationMethodType.U2F), + "/u2f/set?" + params, + ); + case SecondFactorType.OTP_EMAIL: + return ( + emailVerified && + EMAIL( + userMethods.includes(AuthenticationMethodType.OTP_EMAIL), + "/otp/email/set?" + params, + ) + ); + case SecondFactorType.OTP_SMS: + return ( + phoneVerified && + SMS( + userMethods.includes(AuthenticationMethodType.OTP_SMS), + "/otp/sms/set?" + params, + ) + ); + default: + return null; + } + })} +
+ {!force && ( + + )} + ); } diff --git a/apps/login/src/lib/server/session.ts b/apps/login/src/lib/server/session.ts index 7ba37011ad..440a3290eb 100644 --- a/apps/login/src/lib/server/session.ts +++ b/apps/login/src/lib/server/session.ts @@ -4,6 +4,7 @@ import { setSessionAndUpdateCookie } from "@/lib/server/cookie"; import { deleteSession, getLoginSettings, + humanMFAInitSkipped, listAuthenticationMethodTypes, } from "@/lib/zitadel"; import { Duration } from "@zitadel/client"; @@ -20,6 +21,53 @@ import { } from "../cookies"; import { getServiceUrlFromHeaders } from "../service"; +export async function skipMFAAndContinueWithNextUrl({ + userId, + requestId, + loginName, + sessionId, + organization, +}: { + userId: string; + loginName?: string; + sessionId?: string; + requestId?: string; + organization?: string; +}) { + const _headers = await headers(); + const { serviceUrl } = getServiceUrlFromHeaders(_headers); + + const loginSettings = await getLoginSettings({ + serviceUrl, + organization: organization, + }); + + const skip = await humanMFAInitSkipped({ serviceUrl, userId }); + + const url = + requestId && sessionId + ? await getNextUrl( + { + sessionId: sessionId, + requestId: requestId, + organization: organization, + }, + loginSettings?.defaultRedirectUri, + ) + : loginName + ? await getNextUrl( + { + loginName: loginName, + organization: organization, + }, + loginSettings?.defaultRedirectUri, + ) + : null; + if (url) { + return { redirect: url }; + } +} + export async function continueWithSession({ requestId, ...session diff --git a/apps/login/src/lib/verify-helper.ts b/apps/login/src/lib/verify-helper.ts index 41a537cde1..704d7bbef6 100644 --- a/apps/login/src/lib/verify-helper.ts +++ b/apps/login/src/lib/verify-helper.ts @@ -199,18 +199,18 @@ export async function checkMFAFactors( !availableMultiFactors.length && session?.factors?.user?.id ) { - const user = await getUserByID({ + const userResponse = await getUserByID({ serviceUrl, userId: session.factors?.user?.id, }); - if ( - user.user?.type?.case === "human" && - user.user?.type?.value.mfaInitSkipped - ) { - const mfaInitSkippedTimestamp = timestampDate( - user.user.type.value.mfaInitSkipped, - ); + const humanUser = + userResponse?.user?.type.case === "human" + ? userResponse?.user.type.value + : undefined; + + if (humanUser?.mfaInitSkipped) { + const mfaInitSkippedTimestamp = timestampDate(humanUser.mfaInitSkipped); const mfaInitSkipLifetimeMillis = Number(loginSettings.mfaInitSkipLifetime.seconds) * 1000 + @@ -219,27 +219,32 @@ export async function checkMFAFactors( const mfaInitSkippedTime = mfaInitSkippedTimestamp.getTime(); const timeDifference = currentTime - mfaInitSkippedTime; - if (timeDifference > mfaInitSkipLifetimeMillis) { - const params = new URLSearchParams({ - loginName: session.factors?.user?.loginName as string, - force: "false", // this defines if the mfa is not forced in the settings and can be skipped - checkAfter: "true", // this defines if the check is directly made after the setup - }); - - if (requestId) { - params.append("requestId", requestId); - } - - if (organization || session.factors?.user?.organizationId) { - params.append( - "organization", - organization ?? (session.factors?.user?.organizationId as string), - ); - } - - // TODO: provide a way to setup passkeys on mfa page? - return { redirect: `/mfa/set?` + params }; + if (!(timeDifference > mfaInitSkipLifetimeMillis)) { + // if the time difference is smaller than the lifetime, skip the mfa setup + return; } } + + // the user has never skipped the mfa init but we have a setting so we redirect + + const params = new URLSearchParams({ + loginName: session.factors?.user?.loginName as string, + force: "false", // this defines if the mfa is not forced in the settings and can be skipped + checkAfter: "true", // this defines if the check is directly made after the setup + }); + + if (requestId) { + params.append("requestId", requestId); + } + + if (organization || session.factors?.user?.organizationId) { + params.append( + "organization", + organization ?? (session.factors?.user?.organizationId as string), + ); + } + + // TODO: provide a way to setup passkeys on mfa page? + return { redirect: `/mfa/set?` + params }; } } diff --git a/apps/login/src/lib/zitadel.ts b/apps/login/src/lib/zitadel.ts index 77d656beda..5e3fdec323 100644 --- a/apps/login/src/lib/zitadel.ts +++ b/apps/login/src/lib/zitadel.ts @@ -457,6 +457,21 @@ export async function getUserByID({ return userService.getUserByID({ userId }, {}); } +export async function humanMFAInitSkipped({ + serviceUrl, + userId, +}: { + serviceUrl: string; + userId: string; +}) { + const userService: Client = await createServiceForHost( + UserService, + serviceUrl, + ); + + return userService.humanMFAInitSkipped({ userId }, {}); +} + export async function verifyInviteCode({ serviceUrl, userId, From 9299a065916f0481b29292fd9d9e53998601b040 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Fri, 28 Feb 2025 16:42:32 +0100 Subject: [PATCH 4/7] cleanup --- apps/login/src/lib/server/password.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/login/src/lib/server/password.ts b/apps/login/src/lib/server/password.ts index 6fd3f67bcd..e5a2f58562 100644 --- a/apps/login/src/lib/server/password.ts +++ b/apps/login/src/lib/server/password.ts @@ -72,7 +72,6 @@ export async function resetPassword(command: ResetPasswordCommand) { return passwordReset({ serviceUrl, - userId, urlTemplate: `${host.includes("localhost") ? "http://" : "https://"}${host}/password/set?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}` + From 87dcefce1a0e0227f9df76fdc0b827ed22ff9e56 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Fri, 28 Feb 2025 16:45:38 +0100 Subject: [PATCH 5/7] cleanup --- apps/login/src/lib/server/session.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/login/src/lib/server/session.ts b/apps/login/src/lib/server/session.ts index 440a3290eb..6a1aa5af9b 100644 --- a/apps/login/src/lib/server/session.ts +++ b/apps/login/src/lib/server/session.ts @@ -42,7 +42,7 @@ export async function skipMFAAndContinueWithNextUrl({ organization: organization, }); - const skip = await humanMFAInitSkipped({ serviceUrl, userId }); + await humanMFAInitSkipped({ serviceUrl, userId }); const url = requestId && sessionId From 5819dc509d95dc48b02743a865e392005c0b0030 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Mon, 3 Mar 2025 08:43:33 +0100 Subject: [PATCH 6/7] edit zitadel default setting for tests --- acceptance/zitadel.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/acceptance/zitadel.yaml b/acceptance/zitadel.yaml index 5a17264eb6..0678e8ff86 100644 --- a/acceptance/zitadel.yaml +++ b/acceptance/zitadel.yaml @@ -16,6 +16,26 @@ FirstInstance: ExpirationDate: 2099-01-01T00:00:00Z DefaultInstance: + LoginPolicy: + AllowUsernamePassword: true # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWUSERNAMEPASSWORD + AllowRegister: true # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWREGISTER + AllowExternalIDP: true # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWEXTERNALIDP + ForceMFA: false # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_FORCEMFA + HidePasswordReset: false # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_HIDEPASSWORDRESET + IgnoreUnknownUsernames: false # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_IGNOREUNKNOWNUSERNAMES + AllowDomainDiscovery: true # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWDOMAINDISCOVERY + # 1 is allowed, 0 is not allowed + PasswordlessType: 1 # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_PASSWORDLESSTYPE + # DefaultRedirectURL is empty by default because we use the Console UI + DefaultRedirectURI: # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_DEFAULTREDIRECTURI + # 240h = 10d + PasswordCheckLifetime: 240h # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_PASSWORDCHECKLIFETIME + # 240h = 10d + ExternalLoginCheckLifetime: 240h # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_EXTERNALLOGINCHECKLIFETIME + # 720h = 30d + MfaInitSkipLifetime: 0h # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_MFAINITSKIPLIFETIME + SecondFactorCheckLifetime: 18h # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_SECONDFACTORCHECKLIFETIME + MultiFactorCheckLifetime: 12h # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_MULTIFACTORCHECKLIFETIME PrivacyPolicy: TOSLink: "https://zitadel.com/docs/legal/terms-of-service" PrivacyLink: "https://zitadel.com/docs/legal/policies/privacy-policy" From 5432a506a8f9c35550510a8cef4f29146c9333e1 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Mon, 3 Mar 2025 08:53:37 +0100 Subject: [PATCH 7/7] update readme --- apps/login/readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/login/readme.md b/apps/login/readme.md index 4df81f9f9d..120fad3cd7 100644 --- a/apps/login/readme.md +++ b/apps/login/readme.md @@ -389,7 +389,6 @@ In future, self service options to jump to are shown below, like: ## Currently NOT Supported -- Login Settings: multifactor init prompt - forceMFA on login settings is not checked for IDPs Also note that IDP logins are considered as valid MFA. An additional MFA check will be implemented in future if enforced.