diff --git a/docs/docs/concepts/features/selfservice.md b/docs/docs/concepts/features/selfservice.md index 1a664e4bce..a39e99ba49 100644 --- a/docs/docs/concepts/features/selfservice.md +++ b/docs/docs/concepts/features/selfservice.md @@ -151,7 +151,9 @@ The current password must be entered first. Users can setup and delete a second factor and FIDO Passkeys (Passwordless). Available authenticators are: -- Mobile one-time password (OTP) (Authenticator Apps) +- Time-based one-time password (TOTP) (Which are Authenticator Apps like Google/Microsoft Authenticator, Authy, etc.) +- One-time password sent as E-Mail +- One-time password sent as SMS - FIDO Universal Second Factor (U2F) (Security Keys, Device, etc.) - FIDO2 WebAuthN (Passkeys) diff --git a/docs/docs/guides/integrate/login-ui/_browser_signin_webauthn.mdx b/docs/docs/guides/integrate/login-ui/_browser_signin_webauthn.mdx new file mode 100644 index 0000000000..25c12a0d76 --- /dev/null +++ b/docs/docs/guides/integrate/login-ui/_browser_signin_webauthn.mdx @@ -0,0 +1,12 @@ + +After starting the WebAuthN authentication on the side of ZITADEL you have to challenge the browser. +To do this you need to call the browser API to get the credentials. +Make sure to send the public key credential request options you got from ZITADEL. + +```javascript +const credential = await navigator.credentials.get({ + publicKey: publicKeyCredentialRequestOptions +}); +``` + +Read the [WebAuthN Guide](https://webauthn.guide/#authentication) for more information about "Authenticating with a WebAuthN Credential". diff --git a/docs/docs/guides/integrate/login-ui/_update_session_webauthn.mdx b/docs/docs/guides/integrate/login-ui/_update_session_webauthn.mdx new file mode 100644 index 0000000000..5f6a800ac9 --- /dev/null +++ b/docs/docs/guides/integrate/login-ui/_update_session_webauthn.mdx @@ -0,0 +1,25 @@ + +Now that you have successfully authenticated in the browser, you can update the session of the user. +Fill the webAuthN checks with the credential assertion data you get from the browser. + +More detailed information about the API: [Update Session Documentation](/apis/resources/session_service/session-service-set-session) + +Example Request: + +```bash +curl --request PATCH \ + --url https://$ZITADEL_DOMAIN/v2alpha/sessions/218480890961985793 \ + --header 'Accept: application/json' \ + --header 'Authorization: Bearer '"$TOKEN"''\ + --header 'Content-Type: application/json' \ + --data '{ + "sessionToken": "yMDi6uVPJAcphbbz0LaxC07ihWkNTe7m0Xqch8SzfM5Cz3HSIQIDZ65x1f5Qal0jxz0MEyo-_zYcUg", + "checks": { + "webAuthN": { + "credentialAssertionData": {} + } + } +}' +``` + + diff --git a/docs/docs/guides/integrate/login-ui/mfa.mdx b/docs/docs/guides/integrate/login-ui/mfa.mdx index 05f2a5fe82..79bccaf74c 100644 --- a/docs/docs/guides/integrate/login-ui/mfa.mdx +++ b/docs/docs/guides/integrate/login-ui/mfa.mdx @@ -5,13 +5,17 @@ sidebar_label: Multi-Factor (MFA) import MfaOptions from './_list-mfa-options.mdx'; import BrowserRegisterWebAuthN from './_browser_register_webauthn.mdx'; +import BrowserSigninWebAuthN from './_browser_signin_webauthn.mdx'; +import UpdateSessionWebAuthN from './_update_session_webauthn.mdx'; Multi-factor authentication (MFA) is a multi-step account authentication which requires to user to enter more than only the password. It is highly recommended to use MFA or [Passkeys](./passkey) to make your user accounts more secure. -ZITADEL supports two different Methods: +ZITADEL supports different Methods: - Time-based one time password (TOTP), which are Authenticator apps like Google/Microsoft Authenticator, Authy, etc +- One-time password sent as SMS +- One-time password sent as E-Mail - Universal Second Factor (U2F), which is authentication with your device like Windows Hello, Apple FaceID, Fingerprint, FIDO2 keys, Yubikey, etc. ## TOTP Registration @@ -78,6 +82,292 @@ curl --request POST \ }' ``` +## TOTP Authentication + +### Flow + +![Authenticate TOTP](/img/guides/login-ui/authenticate-totp-flow.png) + +### Check User + +To be able to check the TOTP you need a session with a checked user. This can either happen before the TOTP check or at the same time. +In this example we do two separate requests. So the first step is to create a new Sessions. + +More detailed information about the API: [Create new session Documentation](/apis/resources/session_service/session-service-create-session) + +Example Request + +```bash +curl --request POST \ + --url https://$ZITADEL_DOMAIN/v2alpha/sessions \ + --header 'Accept: application/json' \ + --header 'Authorization: Bearer '"$TOKEN"'' \ + --header 'Content-Type: application/json' \ + --data '{ + "checks": { + "user": { + "loginName": "minnie-mouse@mouse.com" + } + } +}' +``` + +Example Response + +```bash +{ + "details": { + "sequence": "580", + "changeDate": "2023-06-14T05:32:39.007096Z", + "resourceOwner": "163840776835432705" + }, + "sessionId": "218480890961985793", + "sessionToken": "yMDi6uVPJAcphbbz0LaxC07ihWkNTe7m0Xqch8SzfM5Cz3HSIQIDZ65x1f5Qal0jxz0MEyo-_zYcUg" +} +``` + +### Check TOTP + +Now you can show the code field to the user, where he has to enter the code from the Authenticator App. +With that code you have to update the existing session with a totp check. + +More detailed information about the API: [Update session Documentation](/apis/resources/session_service/session-service-set-session) + +Example Request +```bash +curl --request PATCH \ + --url https://$ZITADEL_DOMAIN/v2alpha/sessions/$SESSION-ID \ + --header 'Accept: application/json' \ + --header 'Content-Type: application/json' \ + --data '{ + "sessionToken": "W3mEoesTiYOsiR1LYUCRw3vaEwXKLGDTsqOV_bkOhlah_-ZbuiLgvnzADwe_iYMusbwkMhp7VfMn8j", + "checks": { + "totp": { + "code": "323764" + }, + } +}' +``` + +## SMS Code Registration + +### Flow + +![Register SMS OTP](/img/guides/login-ui/register-phone-otp-flow.png) + +### List the Possible Methods + + + +### Add Phone Number + +When the user has decided to register the phone number to get a code as a second factor, the first step is to add a verified phone number to the user. +If the user already has a verified phone number you can skip this step. + +When adding a new phone number, you can choose if you want ZITADEL to send the verification code to the number, or if you want to send it by yourself. +If ZITADEL should do it, make sure that you have registered an [SMS Provider](/docs/guides/manage/console/instance-settings#sms) and send an empty sendCode object in the request. +With an empty returnCode object in the request, ZITADEL will not send the code, but return it in the response. + +If you don't want the user to verify the phone number, you can also create it directly as verified, by sending the isVerified attribute. + +More detailed information about the API: [Add phone](/apis/resources/user_service/user-service-set-phone) + +Example Request: + +```bash +curl --request POST \ + --url https://$ZITADEL_DOMAIN/v2alpha/users/$USER-ID/phone \ + --header 'Accept: application/json' \ + --header 'Authorization: Bearer '"$TOKEN"'' \ + --header 'Content-Type: application/json' \ + --data '{ + "phone": "+41791234567", + "sendCode": {} + }' +``` + +### Verify Phone Number + +The next step is to show a screen, so the user is able to enter the code for verifying the phone number. +Send a verify phone request with the code in the body. + +More detailed information about the API: [Verify phone](/apis/resources/user_service/user-service-verify-phone) + +Example Request: +```bash +curl --request POST \ + --url https://$ZITADEL_DOMAIN/v2alpha/users/$USER-ID/phone/verify \ + --header 'Accept: application/json' \ + --header 'Authorization: Bearer '"$TOKEN"'' \ + --header 'Content-Type: application/json' \ + --data '{ + "verificationCode": "VBQREB" + }' +``` + +### Add OTP SMS to the user + +Now that the user has a verified phone number you can enable SMS OTP on the user. + +More detailed information about the API: [Add OTP SMS for a user](/apis/resources/user_service/user-service-add-otpsms) + +Example Request: +```bash +curl --request POST \ + --url https://$ZITADEL_DOMAIN/v2alpha/users/$USER-ID/otp_sms \ + --header 'Accept: application/json' \ + --header 'Authorization: Bearer '"$TOKEN"'' \ + --header 'Content-Type: application/json' +``` + +## SMS Code Authentication + +### Flow + +![Authenticate SMS OTP](/img/guides/login-ui/authenticate-phone-otp-flow.png) + +### Check User + +To be able to check the SMS Code you need a session with a checked user. +When creating the session you can already start the sms challenge, this will only be executed if the user check was successful. +You can tell the challenge, if the code should be returned (returnCode: true) or if ZITADEL should send it (returnCode: false). + +More detailed information about the API: [Create new session Documentation](/apis/resources/session_service/session-service-create-session) + +Example Request + +```bash +curl --request POST \ + --url https://$ZITADEL_DOMAIN/v2alpha/sessions \ + --header 'Accept: application/json' \ + --header 'Authorization: Bearer '"$TOKEN"'' \ + --header 'Content-Type: application/json' \ + --data '{ + "checks": { + "user": { + "loginName": "minni-mouse@mouse.com" + } + }, + "challenges": { + "otpSms": { + "returnCode": false + } + } +}' +``` + +### Check SMS Code + +In the next step you should show the user a code field, where he can enter the code, he got as SMS. +The update session request has a check otpSMS where you should send the code, the user has entered. + +Example Request + +```bash +curl --request PATCH \ + --url https://$ZITADEL_DOMAIN/v2alpha/sessions/225307381909694507 \ + --header 'Accept: application/json' \ + --header 'Authorization: Bearer '"$TOKEN"'' \ + --header 'Content-Type: application/json' \ + --data '{ + "sessionToken": "W3mEoesTiYOsiR1LYUCRw3WaFwXKLGDRsqOV_bkOhlah_-ZpuiLgvnzADwe_iYMusbwkMhp7VfMn8g", + "checks": { + "otpSms": { + "code": "3237642" + }, + } +}' +``` + +## Email Code Registration + +### Flow + +![Register Email OTP](/img/guides/login-ui/register-email-otp-flow.png) + +### List the Possible Methods + + + +### Verified Email + +As ZITADEL required all users to have a verified email address, you do not need to add a new email and verify it for setting up the second factor. +For the Email second factor the already verified email address will be taken. + +### Add OTP Email to the user + +As the user already has a verified E-Mail address you can enable E-Mail OTP on the user. + +More detailed information about the API: [Add OTP Email for a user](/apis/resources/user_service/user-service-add-otp-email) + +Example Request: +```bash +curl --request POST \ + --url https://$ZITADEL_DOMAIN/v2alpha/users/$USER-ID/otp_email \ + --header 'Accept: application/json' \ + --header 'Authorization: Bearer '"$TOKEN"'' \ + --header 'Content-Type: application/json' +``` + +## Email Code Authentication + +### Flow + +![Authenticate OTP Email](/img/guides/login-ui/authenticate-email-otp-flow.png) + +### Check User + +To be able to check the Email Code you need a session with a checked user. +When creating the session you can already start the sms challenge, this will only be executed if the user check was successful. +You can tell the challenge, if the code should be returned (returnCode: true) or if ZITADEL should send it (returnCode: false). + +More detailed information about the API: [Create new session Documentation](/apis/resources/session_service/session-service-create-session) + +Example Request + +```bash +curl --request POST \ + --url https://$ZITADEL_DOMAIN/v2alpha/sessions \ + --header 'Accept: application/json' \ + --header 'Authorization: Bearer '"$TOKEN"'' \ + --header 'Content-Type: application/json' \ + --data '{ + "checks": { + "user": { + "loginName": "minni-mouse@mouse.com" + } + }, + "challenges": { + "otpEmail": { + "returnCode": false + } + } +}' +``` + +### Check Email Code + +In the next step you should show the user a code field, where he can enter the code, he got per E-Mail. +The update session request has a check otpEmail where you should send the code, the user has entered. + +Example Request + +```bash +curl --request PATCH \ + --url https://$ZITADEL_DOMAIN/v2alpha/sessions/225307381909694507 \ + --header 'Accept: application/json' \ + --header 'Authorization: Bearer '"$TOKEN"'' \ + --header 'Content-Type: application/json' \ + --data '{ + "sessionToken": "W3mEoesTiYOsiR1LYUCRw3WaFwXKLGDRsqOV_bkOhlah_-ZpuiLgvnzADwe_iYMusbwkMhp7VfMn8g", + "checks": { + "otpEmail": { + "code": "3237642" + }, + } +}' +``` + ## U2F Registration ### Flow @@ -181,7 +471,7 @@ curl --request POST \ "rawId": "pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00", "response": { "attestationObject": "o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIgRKS3VpeE9tfExXRzkoUKnG4rQWPvtSSt4YtDGgTx32oCIQDPey-2YJ4uIg-QCM4jj6aE2U3tgMFM_RP7Efx6xRu3JGhhdXRoRGF0YVikSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAADju76085Yhmlt1CEOHkwLQAIKWsFWqxeMT8SxZnwp0ZMF1nk6yhs2m3AIvdixCNVgtNpQECAyYgASFYIMGUDSP2FAQn2MIfPMy7cyB_Y30VqixVgGULTBtFjfRiIlggjUGfQo3_-CrMmH3S-ZQkFKWKnNBQEAMkFtG-9A4zqW0", - "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiQlhXdHh0WGxJeFZZa0pHT1dVaUVmM25zby02aXZKdWw2YmNmWHdMVlFIayIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjgwODAifQ" + "clientDataJSON": "eyJ0eXBlJjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiQlhXdHh0WGxJeFZZa0pHT1dVaUVmM25zby02aXZKdWw2YmNmWHdMVlFIayIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjgwODAifQ" } }, "tokenName": "Google Pixel" @@ -189,3 +479,65 @@ curl --request POST \ ``` You have successfully registered a new U2F to the user. + +## U2F Authentication + +### Flow + +![Authenticate U2F](/img/guides/login-ui/authenticate-u2f-flow.png) + +### Check User + +To be able to check the Universal-Second-Factor (U2F) you need a user check and a webAuthN challenge. +In the creat session request you can check for the user and directly initiate the webAuthN challenge. + +For U2F you can choose between "USER_VERIFICATION_REQUIREMENT_PREFERRED" and "USER_VERIFICATION_REQUIREMENT_DISCOURAGED" for the challenge. +Best practice is using discouraged, as this doesn't require the user to enter a PIN. With `preferred` the user might be prompted for the PIN, but it is not necessary. + +More detailed information about the API: [Create new session Documentation](/apis/resources/session_service/session-service-create-session) + +Example Request + +```bash +curl --request POST \ + --url https://$ZITADEL_DOMAIN/v2alpha/sessions \ + --header 'Accept: application/json' \ + --header 'Authorization: Bearer '"$TOKEN"'' \ + --header 'Content-Type: application/json' \ + --data '{ + "checks": { + "user": { + "loginName": "minni-mouse@mouse.com" + } + }, + "metadata": {}, + "challenges": { + "webAuthN": { + "domain": "YOUR-Domain", + "userVerificationRequirement": "USER_VERIFICATION_REQUIREMENT_DISCOURAGED" + } + } +}' +``` + +Example Response + +```bash +{ + "details": { + "sequence": "580", + "changeDate": "2023-06-14T05:32:39.007096Z", + "resourceOwner": "163840776835432705" + }, + "sessionId": "218480890961985793", + "sessionToken": "yMDi6uVPJAcphbbz0LaxC07ihWkNTe7m0Xqch8SzfM5Cz3HSIQIDZ65x1f5Qal0jxz0MEyo-_zYcUg" +} +``` + +### Signin in Browser + + + +### Update Session with WebAuthN + + \ No newline at end of file diff --git a/docs/docs/guides/integrate/login-ui/passkey.mdx b/docs/docs/guides/integrate/login-ui/passkey.mdx index 247d8229bb..051b8ddfd2 100644 --- a/docs/docs/guides/integrate/login-ui/passkey.mdx +++ b/docs/docs/guides/integrate/login-ui/passkey.mdx @@ -3,6 +3,10 @@ title: Passkeys sidebar_label: Passkeys --- +import BrowserRegisterWebAuthN from './_browser_register_webauthn.mdx'; +import BrowserSigninWebAuthN from './_browser_signin_webauthn.mdx'; +import UpdateSessionWebAuthN from './_update_session_webauthn.mdx'; + Passkeys are a replacement for passwords that provide faster, easier, and more secure sign-ins to websites and apps even across multiple devices. Unlike passwords, passkeys are phishing-resistant and can improve the user experience and security at the same time. Passkeys and there underlying protocols are a standard defined by the [FIDO Standard](https://fidoalliance.org/) . @@ -208,7 +212,6 @@ Next step is to authenticate the user with the new registered passkey. ![Passkey Login](/img/guides/login-ui/passkey-login-flow.png) - ### Create Session First step is to ask the user for his username and create a new session with the ZITADEL API. @@ -273,43 +276,11 @@ Example Response: ### Signing in Browser -After starting the passkey authentication on the side of ZITADEL you have to challenge the browser. -To do this you need to call the browser API to get the credentials. -Make sure to send the public key credential request options you got from ZITADEL. + -```javascript -const credential = await navigator.credentials.get({ - publicKey: publicKeyCredentialRequestOptions -}); -``` - -Read the [WebAuthN Guide](https://webauthn.guide/#authentication) for more information about "Authenticating with a WebAuthN Credential". - -### Update Session with Passkey - -Now that you have successfully authenticated in the browser, you can update the session of the user. -Fill the passkey checks with the credential assertion data you get from the browser. - -More detailed information about the API: [Update Session Documentation](/apis/resources/session_service/session-service-set-session) - -Example Request: - -```bash -curl --request PATCH \ - --url https://$ZITADEL_DOMAIN/v2alpha/sessions/218480890961985793 \ - --header 'Accept: application/json' \ - --header 'Authorization: Bearer '"$TOKEN"''\ - --header 'Content-Type: application/json' \ - --data '{ - "sessionToken": "yMDi6uVPJAcphbbz0LaxC07ihWkNTe7m0Xqch8SzfM5Cz3HSIQIDZ65x1f5Qal0jxz0MEyo-_zYcUg", - "checks": { - "webAuthN": { - "credentialAssertionData": {} - } - } -}' -``` +### Update Session with WebAuthN + diff --git a/docs/docs/guides/integrate/login-ui/username-password.mdx b/docs/docs/guides/integrate/login-ui/username-password.mdx index 6729760ce2..f3a4cfce17 100644 --- a/docs/docs/guides/integrate/login-ui/username-password.mdx +++ b/docs/docs/guides/integrate/login-ui/username-password.mdx @@ -81,8 +81,8 @@ Return Code: To check what is allowed on your instance, call the settings service for more information. The following requests can be useful for registration: -- [Get Login Settings](https://zitadel.com/docs/apis/resources/settings_service/settings-service-get-login-settings) To find out which authentication possibilities are enabled (password, identity provider, etc.) -- [Get Password Complexity Settings](https://zitadel.com/docs/apis/resources/settings_service/settings-service-get-password-complexity-settings) to find out how the password should look like (length, characters, etc.) +- [Get Login Settings](/apis/resources/settings_service/settings-service-get-login-settings) To find out which authentication possibilities are enabled (password, identity provider, etc.) +- [Get Password Complexity Settings](/apis/resources/settings_service/settings-service-get-password-complexity-settings) to find out how the password should look like (length, characters, etc.) ## Create Session with User Check @@ -96,9 +96,9 @@ The create and update session endpoints will always return a session ID and an o If you do not rely on the OIDC standard you can directly use the token. Send it to the Get Session Endpoint to find out how the user has authenticated. -- [Create new session Documentation](https://zitadel.com/docs/apis/resources/session_service/session-service-create-session) -- [Update an existing session Documentation](https://zitadel.com/docs/apis/resources/session_service/session-service-set-session) -- [Get Session Documentation](https://zitadel.com/docs/apis/resources/session_service/session-service-get-session) +- [Create new session Documentation](/apis/resources/session_service/session-service-create-session) +- [Update an existing session Documentation](/apis/resources/session_service/session-service-set-session) +- [Get Session Documentation](/apis/resources/session_service/session-service-get-session) ### Request diff --git a/docs/static/img/guides/login-ui/authenticate-email-otp-flow.png b/docs/static/img/guides/login-ui/authenticate-email-otp-flow.png new file mode 100644 index 0000000000..bf940aff16 Binary files /dev/null and b/docs/static/img/guides/login-ui/authenticate-email-otp-flow.png differ diff --git a/docs/static/img/guides/login-ui/authenticate-phone-otp-flow.png b/docs/static/img/guides/login-ui/authenticate-phone-otp-flow.png new file mode 100644 index 0000000000..652344f8dd Binary files /dev/null and b/docs/static/img/guides/login-ui/authenticate-phone-otp-flow.png differ diff --git a/docs/static/img/guides/login-ui/authenticate-totp-flow.png b/docs/static/img/guides/login-ui/authenticate-totp-flow.png new file mode 100644 index 0000000000..4d0fa751be Binary files /dev/null and b/docs/static/img/guides/login-ui/authenticate-totp-flow.png differ diff --git a/docs/static/img/guides/login-ui/authenticate-u2f-flow.png b/docs/static/img/guides/login-ui/authenticate-u2f-flow.png new file mode 100644 index 0000000000..14e9093965 Binary files /dev/null and b/docs/static/img/guides/login-ui/authenticate-u2f-flow.png differ diff --git a/docs/static/img/guides/login-ui/register-email-otp-flow.png b/docs/static/img/guides/login-ui/register-email-otp-flow.png new file mode 100644 index 0000000000..4285e73af3 Binary files /dev/null and b/docs/static/img/guides/login-ui/register-email-otp-flow.png differ diff --git a/docs/static/img/guides/login-ui/register-phone-otp-flow.png b/docs/static/img/guides/login-ui/register-phone-otp-flow.png new file mode 100644 index 0000000000..3fd67bd4bf Binary files /dev/null and b/docs/static/img/guides/login-ui/register-phone-otp-flow.png differ diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index abaf8d6baf..7b3058844e 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -8006,6 +8006,7 @@ message UpdateHumanProfileRequest { ]; string display_name = 5 [ (validate.rules).string = {min_len: 1, max_len: 200}, + (google.api.field_behavior) = REQUIRED, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { min_length: 1; max_length: 200; diff --git a/proto/zitadel/user/v2alpha/user_service.proto b/proto/zitadel/user/v2alpha/user_service.proto index 4168251712..51d3b2d1cb 100644 --- a/proto/zitadel/user/v2alpha/user_service.proto +++ b/proto/zitadel/user/v2alpha/user_service.proto @@ -202,8 +202,8 @@ service UserService { }; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { - summary: "Change the user phone"; - description: "Change the phone number of a user. If the state is set to not verified, a verification code will be generated, which can be either returned or sent to the user by sms." + summary: "Set the user phone"; + description: "Set the phone number of a user. If the state is set to not verified, a verification code will be generated, which can be either returned or sent to the user by sms." responses: { key: "200" value: {