password page changes, doc

This commit is contained in:
peintnermax
2024-09-16 11:36:44 +02:00
parent 52ebcece0c
commit 70a1ca25d9
7 changed files with 60 additions and 46 deletions

View File

@@ -80,3 +80,25 @@ If no previous condition is met we throw an error stating the user was not found
**EXCEPTIONS:** If the outcome after this order produces a no authentication methods found, or user not found, we check whether `loginSettings?.ignoreUnknownUsernames` is set to `true` as in this case we redirect to the /password page regardless (to not leak information about a registered user).
> NOTE: We ignore `loginSettings.allowExternalIdp` as the information whether IDPs are available comes as response from `getActiveIdentityProviders(org?)`.
### /password
<img src="./screenshots/password.png" alt="/password" width="400px" />
This page shows a password field to hydrate the current session with password as a factor.
Below the password field, a reset password link is shown which allows to send a reset email.
Requests to the APIs made:
- `getLoginSettings(org?)`
- `getBrandingSettings(org?)`
- `listAuthenticationMethodTypes`
**MFA AVAILABLE:** After the password has been submitted, additional authentication Methods are loaded.
If the user has set up an additional **single** second factor, it is redirected to add the next factor. Depending on the available method he is redirected to `/otp/time-based`,`/otp/sms?`, `/otp/email?` or `/u2f?`. If the user has multiple second factors, he is redirected to `/mfa` to select his preferred method to continue.
**NO MFA, FORCE MFA:** If no MFA method is available, and the settings force MFA, the user is sent to `/mfa/set` which prompts to setup a second factor.
**PROMPT PASSKEY** If the settings do not enforce MFA, we check if passkeys are allowed with `loginSettings?.passkeysType === PasskeysType.ALLOWED` and redirect the user to `/passkey/add` if no passkeys are setup. This step can be skipped.
If none of the previous conditions apply, we continue to sign in.

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@@ -18,8 +18,14 @@ export default async function Page({
}: {
searchParams: Record<string | number | symbol, string | undefined>;
}) {
const { loginName, checkAfter, authRequestId, organization, sessionId } =
searchParams;
const {
loginName,
checkAfter,
force,
authRequestId,
organization,
sessionId,
} = searchParams;
const sessionWithData = sessionId
? await loadSessionById(sessionId, organization)

View File

@@ -10,18 +10,17 @@ export default async function Page({
}: {
searchParams: Record<string | number | symbol, string | undefined>;
}) {
const { loginName, promptPasswordless, organization, authRequestId } =
searchParams;
const { loginName, prompt, organization, authRequestId } = searchParams;
const session = await loadMostRecentSession({
loginName,
organization,
});
const title = !!promptPasswordless
const title = !!prompt
? "Authenticate with a passkey"
: "Use your passkey to confirm it's really you";
const description = !!promptPasswordless
const description = !!prompt
? "When set up, you will be able to authenticate without a password."
: "Your device will ask for your fingerprint, face, or screen lock";
@@ -68,7 +67,7 @@ export default async function Page({
{session?.id && (
<RegisterPasskey
sessionId={session.id}
isPrompt={!!promptPasswordless}
isPrompt={!!prompt}
organization={organization}
authRequestId={authRequestId}
/>

View File

@@ -4,14 +4,14 @@ import Alert from "@/ui/Alert";
import DynamicTheme from "@/ui/DynamicTheme";
import PasswordForm from "@/ui/PasswordForm";
import UserAvatar from "@/ui/UserAvatar";
import { PasskeysType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
export default async function Page({
searchParams,
}: {
searchParams: Record<string | number | symbol, string | undefined>;
}) {
const { loginName, organization, promptPasswordless, authRequestId, alt } =
searchParams;
const { loginName, organization, authRequestId, alt } = searchParams;
const sessionFactors = await loadMostRecentSession({
loginName,
@@ -51,7 +51,9 @@ export default async function Page({
authRequestId={authRequestId}
organization={organization}
loginSettings={loginSettings}
promptPasswordless={promptPasswordless === "true"}
promptPasswordless={
loginSettings?.passkeysType === PasskeysType.ALLOWED
}
isAlternative={alt === "true"}
/>
)}

View File

@@ -1,6 +1,5 @@
"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 { redirect } from "next/navigation";
@@ -108,13 +107,6 @@ export async function sendLoginname(command: SendLoginnameCommand) {
command.organization ?? session.factors?.user?.organizationId;
}
if (
loginSettings?.passkeysType &&
loginSettings?.passkeysType === PasskeysType.ALLOWED
) {
paramsPassword.promptPasswordless = `true`;
}
if (command.authRequestId) {
paramsPassword.authRequestId = command.authRequestId;
}
@@ -164,10 +156,6 @@ export async function sendLoginname(command: SendLoginnameCommand) {
// user has no passkey setup and login settings allow passkeys
const paramsPasswordDefault: any = { loginName: command.loginName };
if (loginSettings?.passkeysType === PasskeysType.ALLOWED) {
paramsPasswordDefault.promptPasswordless = `true`; // PasskeysType.PASSKEYS_TYPE_ALLOWED,
}
if (command.authRequestId) {
paramsPasswordDefault.authRequestId = command.authRequestId;
}
@@ -235,10 +223,6 @@ export async function sendLoginname(command: SendLoginnameCommand) {
if (loginSettings?.ignoreUnknownUsernames) {
const paramsPasswordDefault: any = { loginName: command.loginName };
if (loginSettings?.passkeysType === PasskeysType.ALLOWED) {
paramsPasswordDefault.promptPasswordless = `true`;
}
if (command.authRequestId) {
paramsPasswordDefault.authRequestId = command.authRequestId;
}

View File

@@ -153,29 +153,10 @@ export default function PasswordForm({
}
return router.push(`/mfa?` + params);
} 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,
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,
force: "true", // this defines if the mfa is forced in the settings
checkAfter: "true", // this defines if the check is directly made after the setup
});
@@ -189,6 +170,26 @@ export default function PasswordForm({
// TODO: provide a way to setup passkeys on mfa page?
return router.push(`/mfa/set?` + params);
} 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 (authRequestId) {
params.append("authRequestId", authRequestId);
}
if (organization) {
params.append("organization", organization);
}
return router.push(`/passkey/add?` + params);
} else if (authRequestId && submitted.sessionId) {
const params = new URLSearchParams({
sessionId: submitted.sessionId,