mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-13 16:42:34 +00:00
otp setup docs
This commit is contained in:
@@ -204,6 +204,8 @@ Requests to the APIs made:
|
|||||||
If the loginname decides to redirect the user to this page, a button to skip appears which will sign the user in afterwards.
|
If the loginname decides to redirect the user to this page, a button to skip appears which will sign the user in afterwards.
|
||||||
After a passkey is registered, we redirect the user to `/passkey` to verify it again and sign in with the new method. The `createPasskeyRegistrationLink()` uses the token of the session which is determined by the flow.
|
After a passkey is registered, we redirect the user to `/passkey` to verify it again and sign in with the new method. The `createPasskeyRegistrationLink()` uses the token of the session which is determined by the flow.
|
||||||
|
|
||||||
|
> NOTE: this page allows passkeys to be created only if the current session is valid (self service), or no authentication method is set (register). TODO: to be implemented.
|
||||||
|
|
||||||
> NOTE: Redirecting the user to `/passkey` will not be required in future and the currently used session will be hydrated directly after registering. (https://github.com/zitadel/zitadel/issues/8611)
|
> NOTE: Redirecting the user to `/passkey` will not be required in future and the currently used session will be hydrated directly after registering. (https://github.com/zitadel/zitadel/issues/8611)
|
||||||
|
|
||||||
### /otp/time-based/set
|
### /otp/time-based/set
|
||||||
@@ -212,10 +214,29 @@ This page registers a time based OTP method for a user.
|
|||||||
|
|
||||||
<img src="./screenshots/otpset.png" alt="/otp/time-based/set" width="400px" />
|
<img src="./screenshots/otpset.png" alt="/otp/time-based/set" width="400px" />
|
||||||
|
|
||||||
|
Requests to the APIs made:
|
||||||
|
|
||||||
|
- `getBrandingSettings(org?)`
|
||||||
|
- `getSession()`
|
||||||
|
- `registerTOTP()`
|
||||||
|
- `verifyTOTP()`
|
||||||
|
|
||||||
|
After the setup is done, the user is redirected to verify the TOTP method on `/otp/time-based`.
|
||||||
|
|
||||||
|
> NOTE: Redirecting the user to `/otp/time-based` will not be required in future and the currently used session will be hydrated directly. (https://github.com/zitadel/zitadel/issues/8611)
|
||||||
|
|
||||||
### /otp/email/set /otp/sms/set
|
### /otp/email/set /otp/sms/set
|
||||||
|
|
||||||
This page registers either an Email OTP method or SMS OTP method for a user.
|
This page registers either an Email OTP method or SMS OTP method for a user.
|
||||||
|
|
||||||
|
Requests to the APIs made:
|
||||||
|
|
||||||
|
- `getBrandingSettings(org?)`
|
||||||
|
- `getSession()`
|
||||||
|
- `addOTPEmail()` / `addOTPSMS()`
|
||||||
|
|
||||||
|
This page directly calls `addOTPEmail()` or `addOTPSMS()` when invoked and shows a success message.
|
||||||
|
|
||||||
### /u2f/set
|
### /u2f/set
|
||||||
|
|
||||||
This page registers a U2F method for a user.
|
This page registers a U2F method for a user.
|
||||||
@@ -239,6 +260,8 @@ This page shows a register page, which gets firstname and lastname of a user as
|
|||||||
|
|
||||||
<img src="./screenshots/register.png" alt="/register" width="400px" />
|
<img src="./screenshots/register.png" alt="/register" width="400px" />
|
||||||
|
|
||||||
|
<img src="./screenshots/register_password.png" alt="register with password" width="400px" />
|
||||||
|
|
||||||
Requests to the APIs made:
|
Requests to the APIs made:
|
||||||
|
|
||||||
- `listOrganizations()` :warning: TODO: determine the default organization if no context is set
|
- `listOrganizations()` :warning: TODO: determine the default organization if no context is set
|
||||||
@@ -275,6 +298,21 @@ Both /success and /failure pages are designed to intercept the responses from th
|
|||||||
|
|
||||||
### /verify
|
### /verify
|
||||||
|
|
||||||
|
This page verifies the email to be valid. It page of the login can also be invoked without an active session.
|
||||||
|
The context of the user is taken from the url and is set in the email template.
|
||||||
|
|
||||||
|
<img src="./screenshots/accounts.png" alt="/accounts" width="400px" />
|
||||||
|
|
||||||
|
Requests to the APIs made:
|
||||||
|
|
||||||
|
- `getBrandingSettings(org?)`
|
||||||
|
- `getLoginSettings(org?)`
|
||||||
|
- `verifyEmail()`
|
||||||
|
|
||||||
|
If the page is invoked with an active session (right after a register with password), the user is signed in or redirected to the loginname if no context is known.
|
||||||
|
|
||||||
|
> NOTE: This page will be extended to support invitations. In such case, authentication methods of the user are loaded and if none available, shown as possible next step (`/passkey/set`, `password/set`).
|
||||||
|
|
||||||
### /accounts
|
### /accounts
|
||||||
|
|
||||||
This page shows an overview of all current sessions.
|
This page shows an overview of all current sessions.
|
||||||
|
|||||||
BIN
apps/login/screenshots/register_password.png
Normal file
BIN
apps/login/screenshots/register_password.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
BIN
apps/login/screenshots/verify.png
Normal file
BIN
apps/login/screenshots/verify.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@@ -31,8 +31,7 @@ export default async function Page({
|
|||||||
organization,
|
organization,
|
||||||
});
|
});
|
||||||
|
|
||||||
let totpResponse: RegisterTOTPResponse | undefined,
|
let totpResponse: RegisterTOTPResponse | undefined, error: Error | undefined;
|
||||||
totpError: Error | undefined;
|
|
||||||
if (session && session.factors?.user?.id) {
|
if (session && session.factors?.user?.id) {
|
||||||
if (method === "time-based") {
|
if (method === "time-based") {
|
||||||
await registerTOTP(session.factors.user.id)
|
await registerTOTP(session.factors.user.id)
|
||||||
@@ -41,15 +40,21 @@ export default async function Page({
|
|||||||
totpResponse = resp;
|
totpResponse = resp;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((err) => {
|
||||||
totpError = error;
|
error = err;
|
||||||
});
|
});
|
||||||
} else if (method === "sms") {
|
} else if (method === "sms") {
|
||||||
// does not work
|
// does not work
|
||||||
await addOTPSMS(session.factors.user.id);
|
await addOTPSMS(session.factors.user.id).catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
error = new Error("Could not add OTP via SMS");
|
||||||
|
});
|
||||||
} else if (method === "email") {
|
} else if (method === "email") {
|
||||||
// works
|
// works
|
||||||
await addOTPEmail(session.factors.user.id);
|
await addOTPEmail(session.factors.user.id).catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
error = new Error("Could not add OTP via Email");
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid method");
|
throw new Error("Invalid method");
|
||||||
}
|
}
|
||||||
@@ -98,9 +103,9 @@ export default async function Page({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{totpError && (
|
{error && (
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<Alert>{totpError?.message}</Alert>
|
<Alert>{error?.message}</Alert>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -119,8 +124,6 @@ export default async function Page({
|
|||||||
Scan the QR Code or navigate to the URL manually.
|
Scan the QR Code or navigate to the URL manually.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
{/* {auth && <div>{auth.to}</div>} */}
|
|
||||||
|
|
||||||
<TOTPRegister
|
<TOTPRegister
|
||||||
uri={totpResponse.uri as string}
|
uri={totpResponse.uri as string}
|
||||||
secret={totpResponse.secret as string}
|
secret={totpResponse.secret as string}
|
||||||
|
|||||||
@@ -74,39 +74,11 @@ export async function addOTPEmail(userId: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addOTPSMS(userId: string, token?: string) {
|
export async function addOTPSMS(userId: string) {
|
||||||
// TODO: Follow up here, I do not understand the branching
|
|
||||||
// let userService;
|
|
||||||
// if (token) {
|
|
||||||
// const authConfig: ZitadelServerOptions = {
|
|
||||||
// name: "zitadel login",
|
|
||||||
// apiUrl: process.env.ZITADEL_API_URL ?? "",
|
|
||||||
// token: token,
|
|
||||||
// };
|
|
||||||
// const sessionUser = initializeServer(authConfig);
|
|
||||||
// userService = user.getUser(sessionUser);
|
|
||||||
// } else {
|
|
||||||
// userService = user.getUser(server);
|
|
||||||
// }
|
|
||||||
|
|
||||||
return userService.addOTPSMS({ userId }, {});
|
return userService.addOTPSMS({ userId }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function registerTOTP(userId: string, token?: string) {
|
export async function registerTOTP(userId: string) {
|
||||||
// TODO: Follow up here, I do not understand the branching
|
|
||||||
// let userService;
|
|
||||||
// if (token) {
|
|
||||||
// const authConfig: ZitadelServerOptions = {
|
|
||||||
// name: "zitadel login",
|
|
||||||
// apiUrl: process.env.ZITADEL_API_URL ?? "",
|
|
||||||
// token: token,
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// const sessionUser = initializeServer(authConfig);
|
|
||||||
// userService = user.getUser(sessionUser);
|
|
||||||
// } else {
|
|
||||||
// userService = user.getUser(server);
|
|
||||||
// }
|
|
||||||
return userService.registerTOTP({ userId }, {});
|
return userService.registerTOTP({ userId }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user