Merge branch 'usage-telemetry' of github.com:zitadel/zitadel into usage-telemetry
@ -0,0 +1,12 @@
|
||||
Now that you have started the registration within ZITADEL, you have to register the credentials in the browser.
|
||||
This requires a call to the browser api and looks something like the following.
|
||||
Make sure to send the public key credential creation options you got in the previous request from ZITADEL.
|
||||
|
||||
```bash
|
||||
const credential = await navigator.credentials.create({
|
||||
publicKey: publicKeyCredentialCreationOptions
|
||||
});
|
||||
```
|
||||
|
||||
For more information about WebAuthN and registering credential flow, read the following guide:
|
||||
[Registering a WebAuthN Credentials](https://webauthn.guide/#registration)
|
46
docs/docs/guides/integrate/login-ui/_list-mfa-options.mdx
Normal file
@ -0,0 +1,46 @@
|
||||
Your user has successfully authenticated, and now you ask him if he wants to setup MFA to have a more secure account.
|
||||
When he starts the configuration, first you want him to show the possible methods.
|
||||
You can either list it implicitly or call the settings service from ZITADEL to get what is configured on the login settings.
|
||||
|
||||
More detailed information about the API: [Get Login Settings Documentation](/apis/resources/settings_service/settings-service-get-login-settings)
|
||||
|
||||
Request Example:
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/settings/login \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''
|
||||
```
|
||||
|
||||
Response Example:
|
||||
The relevant part for the list is the second factor and multi factor list.
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "293",
|
||||
"changeDate": "2023-03-29T14:16:55.570482Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
},
|
||||
"settings": {
|
||||
"allowUsernamePassword": true,
|
||||
"allowRegister": true,
|
||||
"allowExternalIdp": true,
|
||||
"passkeysType": "PASSKEYS_TYPE_ALLOWED",
|
||||
"passwordCheckLifetime": "864000s",
|
||||
"externalLoginCheckLifetime": "864000s",
|
||||
"mfaInitSkipLifetime": "2592000s",
|
||||
"secondFactorCheckLifetime": "64800s",
|
||||
"multiFactorCheckLifetime": "43200s",
|
||||
"secondFactors": [
|
||||
"SECOND_FACTOR_TYPE_OTP",
|
||||
"SECOND_FACTOR_TYPE_U2F"
|
||||
],
|
||||
"multiFactors": [
|
||||
"MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION"
|
||||
],
|
||||
"resourceOwnerType": "RESOURCE_OWNER_TYPE_ORG"
|
||||
}
|
||||
}
|
||||
```
|
191
docs/docs/guides/integrate/login-ui/mfa.mdx
Normal file
@ -0,0 +1,191 @@
|
||||
---
|
||||
title: Multi-Factor (MFA)
|
||||
sidebar_label: Multi-Factor (MFA)
|
||||
---
|
||||
|
||||
import MfaOptions from './_list-mfa-options.mdx';
|
||||
import BrowserRegisterWebAuthN from './_browser_register_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:
|
||||
- Time-based one time password (TOTP), which are Authenticator apps like Google/Microsoft Authenticator, Authy, etc
|
||||
- Universal Second Factor (U2F), which is authentication with your device like Windows Hello, Apple FaceID, Fingerprint, FIDO2 keys, Yubikey, etc.
|
||||
|
||||
## TOTP Registration
|
||||
|
||||
### Flow
|
||||
|
||||
![Register TOTP](/img/guides/login-ui/register-totp-flow.png)
|
||||
|
||||
### List the Possible Methods
|
||||
|
||||
<MfaOptions/>
|
||||
|
||||
### Start TOTP Registration
|
||||
|
||||
The user has selected to setup Time-based One-Time-Password (TOTP).
|
||||
To show the user the QR to register TOTP with his Authenticator App like Google/Microsoft Authenticator or Authy you have to start the registration on the ZITADEL API.
|
||||
Generate the QR Code with the URI from the response.
|
||||
For users that do not have a QR Code reader make sure to also show the secret, to enable manual configuration.
|
||||
|
||||
More detailed information about the API: [Start TOTP Registration Documentation](/apis/resources/user_service/user-service-register-totp)
|
||||
|
||||
Request Example:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/totp \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
Response Example:
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "2",
|
||||
"changeDate": "2023-06-28",
|
||||
"resourceOwner": "69629023906488334"
|
||||
},
|
||||
"uri": "otpauth://totp/ZITADEL:minni-mouse@mouse.com?algorithm=SHA1&digits=6&issuer=ZITADEL&period=30&secret=TJOPWSDYILLHXFV4MLKNNJOWFG7VSDCK",
|
||||
"secret": "TJOPWSDYILLHXFV4MLKNNJOWFG7VSDCK"
|
||||
}
|
||||
```
|
||||
|
||||
### Verify TOTP Registration
|
||||
|
||||
When the user has added the account to his authenticator app, he has to enter the code from the App to finish the registration.
|
||||
This code has to be sent to the verify endpoint in ZITADEL.
|
||||
|
||||
More detailed information about the API: [Verify TOTP Documentation](/apis/resources/user_service/user-service-verify-totp-registration)
|
||||
|
||||
Request Example:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/totp/_verify \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"code": "123456"
|
||||
}'
|
||||
```
|
||||
|
||||
## U2F Registration
|
||||
|
||||
### Flow
|
||||
|
||||
![Register U2F](/img/guides/login-ui/register-u2f-flow.png)
|
||||
|
||||
### List the Possible Methods
|
||||
|
||||
<MfaOptions/>
|
||||
|
||||
### Start U2F Registration
|
||||
|
||||
The user has selected to setup Universal Second Factor (U2F).
|
||||
To be able to authenticate in the browser you have to start the u2f registration within ZITADEL.
|
||||
|
||||
More detailed information about the API: [Start U2F Registration Documentation](/apis/resources/user_service/user-service-register-u-2-f)
|
||||
|
||||
Request Example:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/u2f \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"domain": "acme.com"
|
||||
}'
|
||||
```
|
||||
|
||||
Response Example:
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "2",
|
||||
"changeDate": "2023-07-03",
|
||||
"resourceOwner": "69629023906488334"
|
||||
},
|
||||
"u2fId": "163840776835432705",
|
||||
"publicKeyCredentialCreationOptions": {
|
||||
"publicKey": {
|
||||
"attestation": "none",
|
||||
"authenticatorSelection": {
|
||||
"userVerification": "required"
|
||||
},
|
||||
"challenge": "XaMYwWOZ5hj6pwtwJJlpcI-ExkO5TxevBMG4R8DoKQQ",
|
||||
"excludeCredentials": [
|
||||
{
|
||||
"id": "tVp1QfYhT8DkyEHVrv7blnpAo2YJzbZgZNBf7zPs6CI",
|
||||
"type": "public-key"
|
||||
}
|
||||
],
|
||||
"pubKeyCredParams": [
|
||||
{
|
||||
"alg": -7,
|
||||
"type": "public-key"
|
||||
}
|
||||
],
|
||||
"rp": {
|
||||
"id": "localhost",
|
||||
"name": "ZITADEL"
|
||||
},
|
||||
"timeout": 300000,
|
||||
"user": {
|
||||
"displayName": "Minie Mouse",
|
||||
"id": "MjE1NTk4MDAwNDY0OTk4OTQw",
|
||||
"name": "minie-mouse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Register new U2F on current device
|
||||
|
||||
<BrowserRegisterWebAuthN/>
|
||||
|
||||
### Verify U2F Registration
|
||||
|
||||
In the next request you have to verify the U2F within ZITADEL.
|
||||
Include the public key credential you got from the browser in your request.
|
||||
You can give the U2F a name, which makes it easier for the user to identify the registered authentication methods.
|
||||
Example: Google Pixel, iCloud Keychain, Yubikey, etc
|
||||
|
||||
More detailed information about the API: [Verify U2F Documentation](/apis/resources/user_service/user-service-verify-u-2-f-registration)
|
||||
|
||||
Example Request:
|
||||
|
||||
```bash
|
||||
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/u2f/$PASSKEY_ID \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"publicKeyCredential": {
|
||||
"type": "public-key",
|
||||
"id": "pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00",
|
||||
"rawId": "pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00",
|
||||
"response": {
|
||||
"attestationObject": "o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIgRKS3VpeE9tfExXRzkoUKnG4rQWPvtSSt4YtDGgTx32oCIQDPey-2YJ4uIg-QCM4jj6aE2U3tgMFM_RP7Efx6xRu3JGhhdXRoRGF0YVikSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAADju76085Yhmlt1CEOHkwLQAIKWsFWqxeMT8SxZnwp0ZMF1nk6yhs2m3AIvdixCNVgtNpQECAyYgASFYIMGUDSP2FAQn2MIfPMy7cyB_Y30VqixVgGULTBtFjfRiIlggjUGfQo3_-CrMmH3S-ZQkFKWKnNBQEAMkFtG-9A4zqW0",
|
||||
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiQlhXdHh0WGxJeFZZa0pHT1dVaUVmM25zby02aXZKdWw2YmNmWHdMVlFIayIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjgwODAifQ"
|
||||
}
|
||||
},
|
||||
"tokenName": "Google Pixel"
|
||||
}'
|
||||
```
|
||||
|
||||
You have successfully registered a new U2F to the user.
|
313
docs/docs/guides/integrate/login-ui/passkey.mdx
Normal file
@ -0,0 +1,313 @@
|
||||
---
|
||||
title: Passkeys
|
||||
sidebar_label: Passkeys
|
||||
---
|
||||
|
||||
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/) .
|
||||
|
||||
## Register Passkey
|
||||
|
||||
### Flow
|
||||
|
||||
![Passkey Registration](/img/guides/login-ui/passkey-registration-flow.png)
|
||||
|
||||
There are two options to onboard users with passkeys:
|
||||
1. You send the user a link (email, sms, ...) with an embedded code, so the user is able to register passkey on any capable device
|
||||
2. You start the passkey registration directly on the current device used by a user
|
||||
|
||||
### Send Registration Link
|
||||
|
||||
When you want to send a link to your user, that enables him to register a new passkey device, you can choose if ZITADEL sends the code or ZITADEL can return the code in the API response. This way you can send a link through any channel of your choice (email, sms, phone, in-person, postal, ...).
|
||||
|
||||
If you asked ZITADEL to send the link to the user please make sure to populate the link with the needed values that point towards your registration UI.
|
||||
|
||||
More detailed information about the API: [Send Registration Link Documentation](/apis/resources/user_service/user-service-create-passkey-registration-link)
|
||||
|
||||
Request Example:
|
||||
Send either the sendLink or the returnCode (empty message) in the request body, depending on the use case you have.
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/passkeys/registration_link \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"sendLink": {
|
||||
"urlTemplate": "https://example.com/passkey/register?userID={{.UserID}}&orgID={{.OrgID}}&codeID={{.CodeID}}&code={{.Code}}"
|
||||
},
|
||||
"returnCode": {}
|
||||
}'
|
||||
```
|
||||
|
||||
Response Example:
|
||||
The code is only filled if returnCode has been requested in the Request.Passkey
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "632",
|
||||
"changeDate": "2023-06-28T08:09:51.257699Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
},
|
||||
"code": {
|
||||
"id": "220526087715684609",
|
||||
"code": "2KEpIeQGSBBd"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Start Passkey Registration
|
||||
|
||||
When starting the passkey registration you can optionally send the registration code from the step above to ZITADEL to pair it with a specific user.
|
||||
|
||||
By specifying the authenticator type you can choose if the passkey should be cross platform or not. Per default all types are allowed:
|
||||
- PASSKEY_AUTHENTICATOR_UNSPECIFIED
|
||||
- PASSKEY_AUTHENTICATOR_PLATFORM
|
||||
- PASSKEY_AUTHENTICATOR_CROSS_PLATFORM
|
||||
|
||||
The API response will provide you the public key credential options, this will be used by the browser to obtain a signed challenge.
|
||||
|
||||
More detailed information about the API: [Start Passkey Registration Documentation](/apis/resources/user_service/user-service-register-passkey)
|
||||
|
||||
Request Example:
|
||||
The code only has to be filled if the user did get a registration code.
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/passkeys \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"code": {
|
||||
"id": "220526087715684609",
|
||||
"code": "2KEpIeQGSBBd"
|
||||
},
|
||||
"authenticator": "PASSKEY_AUTHENTICATOR_UNSPECIFIED"
|
||||
}'
|
||||
```
|
||||
|
||||
Response Example:
|
||||
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "633",
|
||||
"changeDate": "2023-06-28T08:10:26.725981Z",
|
||||
"resourceOwner": "163840776835432705"
|
||||
},
|
||||
"passkeyId": "220526147258024193",
|
||||
"publicKeyCredentialCreationOptions": {
|
||||
"publicKey": {
|
||||
"attestation": "none",
|
||||
"authenticatorSelection": {
|
||||
"userVerification": "required"
|
||||
},
|
||||
"challenge": "JM1uLbVQR2xZJ210DA7E-3j0Cd9rHKUSmc8NyIJBtAY",
|
||||
"pubKeyCredParams": [
|
||||
{
|
||||
"alg": -7,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -35,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -36,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -257,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -258,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -259,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -37,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -38,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -39,
|
||||
"type": "public-key"
|
||||
},
|
||||
{
|
||||
"alg": -8,
|
||||
"type": "public-key"
|
||||
}
|
||||
],
|
||||
"rp": {
|
||||
"id": "example.domain.com",
|
||||
"name": "ZITADEL"
|
||||
},
|
||||
"timeout": 300000,
|
||||
"user": {
|
||||
"displayName": "Minnie Mouse",
|
||||
"id": "MjE4NjYyNTk2OTE4NjQwODk3",
|
||||
"name": "minni-mouse@mouse.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Register new Passkey on current device
|
||||
|
||||
<BrowserRegisterWebAuthN/>
|
||||
|
||||
### Verify Passkey in ZITADEL
|
||||
|
||||
In the next request you have to verify the Passkey within ZITADEL.
|
||||
Include the public key credential you got from the browser in your request.
|
||||
You can give the Passkey a name, which makes it easier for the user to identify the registered authentication methods.
|
||||
Example: Google Pixel, iCloud Keychain, Yubikey, etc
|
||||
|
||||
More detailed information about the API: [Verify Passkey Registration Documentation](/apis/resources/user_service/user-service-verify-passkey-registration)
|
||||
|
||||
Example Request:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://$ZITADEL_DOMAIN/v2alpha/users/$USER_ID/passkeys/$PASSKEY_ID \
|
||||
--header 'Accept: application/json' \
|
||||
--header 'Authorization: Bearer '"$TOKEN"''\
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"publicKeyCredential": {
|
||||
"type": "public-key",
|
||||
"id": "pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00",
|
||||
"rawId": "pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00",
|
||||
"response": {
|
||||
"attestationObject": "o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIgRKS3VpeE9tfExXRzkoUKnG4rQWPvtSSt4YtDGgTx32oCIQDPey-2YJ4uIg-QCM4jj6aE2U3tgMFM_RP7Efx6xRu3JGhhdXRoRGF0YVikSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAADju76085Yhmlt1CEOHkwLQAIKWsFWqxeMT8SxZnwp0ZMF1nk6yhs2m3AIvdixCNVgtNpQECAyYgASFYIMGUDSP2FAQn2MIfPMy7cyB_Y30VqixVgGULTBtFjfRiIlggjUGfQo3_-CrMmH3S-ZQkFKWKnNBQEAMkFtG-9A4zqW0",
|
||||
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiQlhXdHh0WGxJeFZZa0pHT1dVaUVmM25zby02aXZKdWw2YmNmWHdMVlFIayIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjgwODAifQ"
|
||||
}
|
||||
},
|
||||
"passkeyName": "Google Pixel"
|
||||
}'
|
||||
```
|
||||
|
||||
You have successfully registered a new passkey to the user.
|
||||
Next step is to authenticate the user with the new registered passkey.
|
||||
|
||||
## Login with Passkey
|
||||
|
||||
### Flow
|
||||
|
||||
![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.
|
||||
When creating the new session make sure to include the challenge for passkey.
|
||||
The response will include the public key credential request options for the passkey in the challenges.
|
||||
|
||||
More detailed information about the API: [Create 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": [
|
||||
"CHALLENGE_KIND_PASSKEY"
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
Example Response:
|
||||
```bash
|
||||
{
|
||||
"details": {
|
||||
"sequence": "2",
|
||||
"changeDate": "2023-06-27",
|
||||
"resourceOwner": "69629023906488334"
|
||||
},
|
||||
"sessionId": "d654e6ba-70a3-48ef-a95d-37c8d8a7901a",
|
||||
"sessionToken": "string",
|
||||
"challenges": {
|
||||
"passkey": {
|
||||
"publicKeyCredentialRequestOptions": {
|
||||
"publicKey": {
|
||||
"allowCredentials": [
|
||||
{
|
||||
"id": "ATmqBg-99qyOZk2zloPdJQyS2R7IkFT7v9Hoos_B_nM",
|
||||
"type": "public-key"
|
||||
}
|
||||
],
|
||||
"challenge": "GAOHYz2jE69kJMYo6Laij8yWw9-dKKgbViNhfuy0StA",
|
||||
"rpId": "example.domain.com",
|
||||
"timeout": 300000,
|
||||
"userVerification": "required"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
```bash
|
||||
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": {
|
||||
"passkey": {
|
||||
"credentialAssertionData": {}
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -15,6 +15,8 @@ The goal is to send the user a verification code, which he can use to verify the
|
||||
|
||||
There are two possible ways: You can either let ZITADEL send the notification with the verification code, or you can ask ZITADEL for returning the code and send the email by yourself.
|
||||
|
||||
[Request Password Reset Documentation](/apis/resources/user_service/user-service-password-reset)
|
||||
|
||||
### ZITADEL sends the verification message
|
||||
|
||||
When you want ZITADEL to send the verification code you can define the notification channel.
|
||||
@ -22,7 +24,6 @@ Per default the verification code will be sent to the email address of the user.
|
||||
|
||||
Make sure to also include the URL Template to customize the reset link in the email sent to the user.
|
||||
|
||||
|
||||
### Request
|
||||
|
||||
```bash
|
||||
@ -82,6 +83,8 @@ From a user experience perspective it is nice to prefill the verification code,
|
||||
As soon as the user has typed the new password, you can send the change password request.
|
||||
The change password request allows you to set a new password for the user.
|
||||
|
||||
[Change Password Documentation](/apis/resources/user_service/user-service-set-password)
|
||||
|
||||
:::note
|
||||
This request can be used in the password reset flow as well as to let your user change the password manually.
|
||||
In this case it requires additionally the current password instead of the verification code.
|
||||
|
@ -0,0 +1,21 @@
|
||||
Upon successful introspection, regardless of the token type or introspection method, a response with the boolean `active` is returned, indicating if the provided token is active and if the requesting client is part of the token audience. If `active` is true, further information will be provided:
|
||||
|
||||
| **Property** | **Description** |
|
||||
| --- | --- |
|
||||
| `aud` | The audience of the token |
|
||||
| `client_id` | The client_id of the application the token was issued to |
|
||||
| `exp` | Time the token expires (as unix time) |
|
||||
| `iat` | Time the token was issued at (as unix time) |
|
||||
| `iss` | Issuer of the token |
|
||||
| `jti` | Unique id of the token |
|
||||
| `nbf` | Time the token must not be used before (as unix time) |
|
||||
| `scope` | Space delimited list of scopes granted to the token |
|
||||
| `token_type` | Type of the inspected token. Value is always Bearer |
|
||||
| `username` | ZITADEL's login name of the user. Consists of username@primarydomain |
|
||||
|
||||
|
||||
Depending on the granted scopes, additional information about the authorized user is provided.
|
||||
|
||||
If the authorization fails, an HTTP 401 with invalid_client will be returned.
|
||||
|
||||
In summary, the introspection endpoint plays a crucial role in validating access tokens, either opaque or JWT, ensuring that they are not revoked.
|
103
docs/docs/guides/integrate/token-introspection/basic-auth.mdx
Normal file
@ -0,0 +1,103 @@
|
||||
---
|
||||
title: Basic authentication
|
||||
---
|
||||
|
||||
import IntrospectionResponse from './_introspection-response.mdx';
|
||||
|
||||
This is a guide on how to secure your API using [Basic Authentication](https://zitadel.com/docs/apis/openidoauth/authn-methods#client-secret-basic).
|
||||
|
||||
## Register the API in ZITADEL
|
||||
|
||||
1. Go to your project and click on the **New** button as shown below.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-1.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
2. Give a name to your application (Test API 2 is the name given below) and select type **API**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-2.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
3. Select **Basic** as the authentication method and click **Continue**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-3.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
4. Now review your configuration and click **Create**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-4.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
5. You will now see the API’s **Client ID** and the **Client Secret**. Copy them and click **Close**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-5.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
6. When you click **URLs** on the left, you will see the relevant OIDC URLs. Note down the **issuer** URL, **token_endpoint** and **introspection_endpoint**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-6.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
7. Also note down the **Resource ID** of your project.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-basic-auth-7.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
## Token introspection
|
||||
|
||||
With Basic Authentication, you will receive a Client ID and Client Secret for your API. Send your client_id and client_secret as a Basic Auth Header in the following format:
|
||||
|
||||
```
|
||||
Authorization: "Basic " + base64( formUrlEncode(client_id) + ":" + formUrlEncode(client_secret) )
|
||||
|
||||
```
|
||||
|
||||
The request from the API to the introspection endpoint should be in the following format:
|
||||
|
||||
```
|
||||
curl --request POST \
|
||||
--url {your_domain}/oauth/v2/introspect \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--header 'Authorization: Basic {your_basic_auth_header}' \
|
||||
--data token=VjVxyCZmRmWYqd3_F5db9Pb9mHR5fqzhn...
|
||||
```
|
||||
|
||||
Here's an example of how this is done in Python code:
|
||||
|
||||
```
|
||||
def introspect_token(self, token_string):
|
||||
url = ZITADEL_INTROSPECTION_URL
|
||||
data = {'token': token_string, 'token_type_hint': 'access_token', 'scope': 'openid'}
|
||||
auth = HTTPBasicAuth(API_CLIENT_ID, API_CLIENT_SECRET)
|
||||
resp = requests.post(url, data=data, auth=auth)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
```
|
||||
|
||||
## Introspection response
|
||||
|
||||
<IntrospectionResponse/>
|
||||
|
||||
Follow this [tutorial](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-basic-authentication) to learn how to register an API application using Basic Auth with ZITADEL and test it.
|
||||
|
@ -0,0 +1,192 @@
|
||||
---
|
||||
title: JSON Web Token profile
|
||||
---
|
||||
|
||||
import IntrospectionResponse from './_introspection-response.mdx';
|
||||
|
||||
This is a guide on how to secure your API using [JSON Web Token (JWT) profile (recommended)](https://zitadel.com/docs/apis/openidoauth/authn-methods#client-secret-basic).
|
||||
|
||||
## Register the API in ZITADEL and generate private and public keys
|
||||
|
||||
1. Go to your project and click on the **New** button as shown below.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-1.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
2. Give a name to your application (Test API is the name given below) and select type **API**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-2.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
3. Select **JWT** as the authentication method and click **Continue**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-3.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
4. Now review your configuration and click **Create**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-4.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
5. You will now see the API’s **Client ID**. You will not see a client secret because we are using a private JWT key.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-5.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
6. Next, we must create the key pairs. Click on **New**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-6.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
7. Select **JSON** as the type of key. You can also set an expiration time for the key or leave it empty. Click on **Add**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-7.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
8. Download the created key by clicking the **Download** button and then click **Close**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-8.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
9. The key will be downloaded.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-9.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
10. When you click on URLs on the left, you will see the relevant OIDC URLs. Note down the **issuer** URL, **token_endpoint** and **introspection_endpoint**.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-10.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
11. The key that you downloaded will be of the following format.
|
||||
```
|
||||
{
|
||||
"type":"application",
|
||||
"keyId":"<YOUR_KEY_ID>",
|
||||
"key":"-----BEGIN RSA PRIVATE KEY-----\<YOUR_PRIVATE_KEY>\n-----END RSA PRIVATE KEY-----\n",
|
||||
"appId":"<YOUR_APP_ID>",
|
||||
"clientId":"<YOUR_CLIENT_ID>"
|
||||
}
|
||||
```
|
||||
12. Also note down the **Resource ID** of your project.
|
||||
|
||||
<img
|
||||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-11.png"
|
||||
width="75%"
|
||||
alt="Register the API"
|
||||
/>
|
||||
|
||||
## Token introspection
|
||||
|
||||
You must send a client_assertion as a JWT signed with the API’s private key for ZITADEL to validate the signature against the registered public key.
|
||||
|
||||
Request parameters:
|
||||
|
||||
| **Parameter** | **Description** |
|
||||
---|---
|
||||
| `client_assertion` | When using JWT profile for token or introspection endpoints, you must provide a JWT as an assertion generated with the structure shown below and signed with the downloaded key. |
|
||||
| `client_assertion_type` | `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
|
||||
|
||||
|
||||
You must create your client assertion JWT with the following format:
|
||||
|
||||
Header:
|
||||
```json
|
||||
{
|
||||
"alg": "RS256",
|
||||
"kid": "81693565968962154" (keyId from your key file)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Payload:
|
||||
```json
|
||||
{
|
||||
"iss": "78366401571920522@acme", (clientId from your key file)
|
||||
"sub": "78366401571920522@acme", (clientId from your key file)
|
||||
"aud": "https://{your_domain}", (your ZITADEL domain/issuer URL)
|
||||
"exp": 1605183582, (Unix timestamp of the expiry)
|
||||
"iat": 1605179982 (Unix timestamp of the creation signing time of the JWT, MUST NOT be older than 1h)
|
||||
}
|
||||
```
|
||||
|
||||
Create the JSON Web Token with the above header and payload, and sign it with the private key in your key file. You can do this programmatically or use tools like [https://github.com/zitadel/zitadel-tools](https://github.com/zitadel/zitadel-tools) and [https://dinochiesa.github.io/jwt/](https://dinochiesa.github.io/jwt/).
|
||||
|
||||
The request from the API to the introspection endpoint should be in the following format:
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url {your_domain}/oauth/v2/introspect \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
|
||||
--data client_assertion=eyJhbGciOiJSUzI1Ni... \
|
||||
--data token=VjVxyCZmRmWYqd3_F5db9Pb9mHR
|
||||
```
|
||||
|
||||
Here's an example of how this is done in Python code:
|
||||
|
||||
```python
|
||||
def introspect_token(self, token_string):
|
||||
#Create JWT for client assertion
|
||||
payload = {
|
||||
"iss": API_PRIVATE_KEY_FILE["client_id"],
|
||||
"sub": API_PRIVATE_KEY_FILE["client_id"],
|
||||
"aud": ZITADEL_DOMAIN,
|
||||
"exp": int(time.time()) + 60 * 60, # Expires in 1 hour
|
||||
"iat": int(time.time())
|
||||
}
|
||||
headers = {
|
||||
"alg": "RS256",
|
||||
"kid": API_PRIVATE_KEY_FILE["key_id"]
|
||||
}
|
||||
jwt_token = jwt.encode(payload, API_PRIVATE_KEY_FILE["private_key"], algorithm="RS256", headers=headers)
|
||||
|
||||
#Send introspection request
|
||||
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||
data = {
|
||||
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
|
||||
"client_assertion": jwt_token,
|
||||
"token": token_string
|
||||
}
|
||||
response = requests.post(ZITADEL_INTROSPECTION_URL, headers=headers, data=data)
|
||||
response.raise_for_status()
|
||||
token_data = response.json()
|
||||
print(f"Token data from introspection: {token_data}")
|
||||
return token_data
|
||||
|
||||
```
|
||||
## Introspection response
|
||||
|
||||
<IntrospectionResponse/>
|
||||
|
||||
Follow this [tutorial](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-jwt) to learn how to register an API application using JWT Profile with ZITADEL and test it.
|
||||
|
@ -150,6 +150,22 @@ module.exports = {
|
||||
"guides/integrate/logout",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Token introspection",
|
||||
link: {
|
||||
type: "generated-index",
|
||||
title: "Token introspection",
|
||||
slug: "/guides/integrate/token-introspection",
|
||||
description:
|
||||
"Token introspection is the process of checking whether an access token is valid and can be used to access protected resources. You have an API that acts as an OAuth resource server and can be accessed by user-facing applications. To validate an access token by calling the ZITADEL introspection API, you can use the JSON Web Token (JWT) Profile (recommended) or Basic Authentication for token introspection. It's crucial to understand that the API is entirely separate from the front end. The API shouldn’t concern itself with the token type received. Instead, it's about how the API chooses to call the introspection endpoint, either through JWT Profile or Basic Authentication. Many APIs assume they might receive a JWT and attempt to verify it based on signature or expiration. However, with ZITADEL, you can send either a JWT or an opaque Bearer token from the client end to the API. This flexibility is one of ZITADEL's standout features.",
|
||||
},
|
||||
collapsed: true,
|
||||
items: [
|
||||
"guides/integrate/token-introspection/private-key-jwt",
|
||||
"guides/integrate/token-introspection/basic-auth",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Authenticate service users",
|
||||
@ -169,19 +185,21 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Build your own Login-UI",
|
||||
label: "Build your own login UI",
|
||||
link: {
|
||||
type: "generated-index",
|
||||
title: "Build your own Login-UI",
|
||||
title: "Build your own login UI",
|
||||
slug: "/guides/integrate/login-ui",
|
||||
description:
|
||||
"In the following guides you will learn how to create your own login ui with our APIs. The different scenarios like username/password, external identity provider, etc will be shown.",
|
||||
"In the following guides you will learn how to create your own login UI with our APIs. The different scenarios like username/password, external identity provider, etc. will be shown."
|
||||
|
||||
},
|
||||
collapsed: true,
|
||||
items: [
|
||||
"guides/integrate/login-ui/username-password",
|
||||
"guides/integrate/login-ui/external-login",
|
||||
"guides/integrate/login-ui/passkey",
|
||||
"guides/integrate/login-ui/mfa",
|
||||
"guides/integrate/login-ui/select-account",
|
||||
"guides/integrate/login-ui/password-reset",
|
||||
"guides/integrate/login-ui/logout",
|
||||
|
BIN
docs/static/img/guides/integrate/token-introspection-basic-auth-1.png
vendored
Normal file
After Width: | Height: | Size: 356 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-basic-auth-2.png
vendored
Normal file
After Width: | Height: | Size: 561 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-basic-auth-3.png
vendored
Normal file
After Width: | Height: | Size: 361 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-basic-auth-4.png
vendored
Normal file
After Width: | Height: | Size: 260 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-basic-auth-5.png
vendored
Normal file
After Width: | Height: | Size: 350 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-basic-auth-6.png
vendored
Normal file
After Width: | Height: | Size: 345 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-basic-auth-7.png
vendored
Normal file
After Width: | Height: | Size: 347 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-jwt-profile-1.png
vendored
Normal file
After Width: | Height: | Size: 310 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-jwt-profile-10.png
vendored
Normal file
After Width: | Height: | Size: 345 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-jwt-profile-11.png
vendored
Normal file
After Width: | Height: | Size: 347 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-jwt-profile-2.png
vendored
Normal file
After Width: | Height: | Size: 627 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-jwt-profile-3.png
vendored
Normal file
After Width: | Height: | Size: 382 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-jwt-profile-4.png
vendored
Normal file
After Width: | Height: | Size: 270 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-jwt-profile-5.png
vendored
Normal file
After Width: | Height: | Size: 353 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-jwt-profile-6.png
vendored
Normal file
After Width: | Height: | Size: 295 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-jwt-profile-7.png
vendored
Normal file
After Width: | Height: | Size: 330 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-jwt-profile-8.png
vendored
Normal file
After Width: | Height: | Size: 356 KiB |
BIN
docs/static/img/guides/integrate/token-introspection-jwt-profile-9.png
vendored
Normal file
After Width: | Height: | Size: 333 KiB |
BIN
docs/static/img/guides/login-ui/passkey-login-flow.png
vendored
Normal file
After Width: | Height: | Size: 251 KiB |
BIN
docs/static/img/guides/login-ui/passkey-registration-flow.png
vendored
Normal file
After Width: | Height: | Size: 241 KiB |
BIN
docs/static/img/guides/login-ui/register-totp-flow.png
vendored
Normal file
After Width: | Height: | Size: 321 KiB |
BIN
docs/static/img/guides/login-ui/register-u2f-flow.png
vendored
Normal file
After Width: | Height: | Size: 406 KiB |