mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 04:07:31 +00:00
feat: support client_credentials for service users (#5134)
Request an access_token for service users with OAuth 2.0 Client Credentials Grant. Added functionality to generate and remove a secret on service users.
This commit is contained in:
@@ -325,6 +325,53 @@ Send a `client_assertion` as JWT for us to validate the signature against the re
|
||||
| refresh_token | An new opaque refresh_token. |
|
||||
| token_type | Type of the `access_token`. Value is always `Bearer` |
|
||||
|
||||
### Client Credentials Grant
|
||||
|
||||
#### Required request Parameters
|
||||
|
||||
| Parameter | Description |
|
||||
| ---------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| grant_type | Must be `client_credentials` |
|
||||
| scope | [Scopes](scopes) you would like to request from ZITADEL. Scopes are space delimited, e.g. `openid profile` |
|
||||
|
||||
Additionally, you need to authenticate your client by either sending `client_id` and `client_secret` as Basic Auth Header.
|
||||
Check [Client Secret Basic Auth Method](authn-methods#client-secret-basic) on how to build it correctly.
|
||||
|
||||
```BASH
|
||||
curl --request POST \
|
||||
--url {your_domain}/oauth/v2/token \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--header 'Authorization: Basic ${BASIC_AUTH}' \
|
||||
--data grant_type=client_credentials \
|
||||
--data scope=openid profile
|
||||
```
|
||||
|
||||
Or you can also send your `client_id` and `client_secret` as parameters in the body:
|
||||
|
||||
| Parameter | Description |
|
||||
| ------------- | -------------------------------- |
|
||||
| client_id | client_id of the application |
|
||||
| client_secret | client_secret of the application |
|
||||
|
||||
```BASH
|
||||
curl --request POST \
|
||||
--url {your_domain}/oauth/v2/token \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--data grant_type=client_credentials \
|
||||
--data client_id=${CLIENT_ID} \
|
||||
--data client_secret=${CLIENT_SECRET} \
|
||||
--data scope=openid profile
|
||||
```
|
||||
|
||||
#### Successful Client Credentials response {#token-client-credentials-response}
|
||||
|
||||
| Property | Description |
|
||||
| ------------ | ------------------------------------------------------------------------------------- |
|
||||
| access_token | An `access_token` as JWT or opaque token |
|
||||
| expires_in | Number of second until the expiration of the `access_token` |
|
||||
| scope | Scopes of the `access_token`. These might differ from the provided `scope` parameter. |
|
||||
| token_type | Type of the `access_token`. Value is always `Bearer` |
|
||||
|
||||
### Error response
|
||||
|
||||
| error_type | Possible reason |
|
||||
|
@@ -8,14 +8,14 @@ For a list of supported or unsupported `Grant Types` please have a look at the t
|
||||
|:------------------------------------------------------|:--------------------|
|
||||
| Authorization Code | yes |
|
||||
| Authorization Code with PKCE | yes |
|
||||
| Client Credentials | no |
|
||||
| Client Credentials | yes |
|
||||
| Device Authorization | under consideration |
|
||||
| Implicit | yes |
|
||||
| JSON Web Token (JWT) Profile | yes |
|
||||
| Refresh Token | yes |
|
||||
| Resource Owner Password Credentials | no |
|
||||
| Security Assertion Markup Language (SAML) 2.0 Profile | no |
|
||||
| Token Exchange | no |
|
||||
| Security Assertion Markup Language (SAML) 2.0 Profile | no |
|
||||
| Token Exchange | no |
|
||||
|
||||
## Authorization Code
|
||||
|
||||
@@ -131,4 +131,4 @@ Find out how to use it on the [token endpoint](endpoints#token_endpoint) or the
|
||||
|
||||
> Due to growing security concerns we do not support this grant type. With OAuth 2.1 it looks like this grant will be removed.
|
||||
|
||||
**Link to spec.** [OThe OAuth 2.0 Authorization Framework Section 1.3.3](https://tools.ietf.org/html/rfc6749#section-1.3.3)
|
||||
**Link to spec.** [OThe OAuth 2.0 Authorization Framework Section 1.3.3](https://tools.ietf.org/html/rfc6749#section-1.3.3)
|
||||
|
@@ -581,6 +581,30 @@ Changes a machine user
|
||||
PUT: /users/{user_id}/machine
|
||||
|
||||
|
||||
### GenerateMachineSecret
|
||||
|
||||
> **rpc** GenerateMachineSecret([GenerateMachineSecretRequest](#generatemachinesecretrequest))
|
||||
[GenerateMachineSecretResponse](#generatemachinesecretresponse)
|
||||
|
||||
Generates and sets a new machine secret
|
||||
|
||||
|
||||
|
||||
PUT: /users/{user_id}/secret
|
||||
|
||||
|
||||
### RemoveMachineSecret
|
||||
|
||||
> **rpc** RemoveMachineSecret([RemoveMachineSecretRequest](#removemachinesecretrequest))
|
||||
[RemoveMachineSecretResponse](#removemachinesecretresponse)
|
||||
|
||||
Removes the machine secret
|
||||
|
||||
|
||||
|
||||
DELETE: /users/{user_id}/secret
|
||||
|
||||
|
||||
### GetMachineKeyByIDs
|
||||
|
||||
> **rpc** GetMachineKeyByIDs([GetMachineKeyByIDsRequest](#getmachinekeybyidsrequest))
|
||||
@@ -4425,6 +4449,30 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### GenerateMachineSecretRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| user_id | string | - | string.min_len: 1<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### GenerateMachineSecretResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| client_id | string | - | |
|
||||
| client_secret | string | - | |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### GenerateOrgDomainValidationRequest
|
||||
|
||||
|
||||
@@ -7175,6 +7223,28 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### RemoveMachineSecretRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| user_id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveMachineSecretResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveMultiFactorFromLoginPolicyRequest
|
||||
|
||||
|
||||
|
@@ -133,6 +133,7 @@ title: zitadel/user.proto
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| name | string | - | |
|
||||
| description | string | - | |
|
||||
| has_secret | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
88
docs/docs/guides/integrate/client-credentials.md
Normal file
88
docs/docs/guides/integrate/client-credentials.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Client Credentials with Service Users
|
||||
---
|
||||
|
||||
This is a guide on how to use Client Credentials with service users in ZITADEL. You can read more about users [here](/concepts/structure/users.md).
|
||||
|
||||
In ZITADEL, the Client Credentials grant can be used for this non-interactive authentication as alternative to the [JWT profile authentication](serviceusers).
|
||||
|
||||
## Create a Service User with a Secret
|
||||
|
||||
1. Navigate to Service Users
|
||||
2. Click on **New**
|
||||
3. Enter a username and a display name
|
||||
4. Click on **Create**
|
||||
5. Open **Actions** in the top right corner and click on **Generate Client Secret**
|
||||
6. Copy the **ClientID** and **ClientSecret** from the dialog
|
||||
|
||||
:::note
|
||||
Be sure to copy in particular the ClientSecret. You won't be able to retrieve it again.
|
||||
If you lose it, you will have to generate a new one.
|
||||
:::
|
||||
|
||||

|
||||
|
||||
## Grant role for ZITADEL
|
||||
|
||||
To be able to access the ZITADEL APIs your service user needs permissions to ZITADEL.
|
||||
|
||||
1. Go to the detail page of your organization
|
||||
2. Click in the top right corner the "+" button
|
||||
3. Search for your service user
|
||||
4. Give the user the role you need, for the example we choose Org Owner (More about [ZITADEL Permissions](../manage/console/managers))
|
||||
|
||||

|
||||
|
||||
## Authenticating a service user
|
||||
|
||||
In this step we will authenticate a service user and receive an access_token to use against the ZITADEL API.
|
||||
|
||||
You will need to craft a POST request to ZITADEL's token endpoint:
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://{your_domain}.zitadel.cloud/oauth/v2/token \
|
||||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||||
--header 'Authorization: Basic ${BASIC_AUTH}' \
|
||||
--data grant_type=client_credentials \
|
||||
--data scope='openid profile email urn:zitadel:iam:org:project:id:zitadel:aud'
|
||||
```
|
||||
|
||||
* `grant_type` should be set to `client_credentials`
|
||||
* `scope` should contain any [Scopes](../../apis/openidoauth/scopes) you want to include, but must include `openid`. For this example, please include `profile`, `email`
|
||||
and `urn:zitadel:iam:org:project:id:zitadel:aud`. The latter provides access to the ZITADEL API.
|
||||
|
||||
You should receive a successful response with `access_token`, `token_type` and time to expiry in seconds as `expires_in`.
|
||||
|
||||
```bash
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"access_token": "MtjHodGy4zxKylDOhg6kW90WeEQs2q...",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 43199
|
||||
}
|
||||
```
|
||||
|
||||
## Call ZITADEL API with Token
|
||||
|
||||
Because the received Token includes the `urn:zitadel:iam:org:project:id:zitadel:aud` scope, we can send it in your requests to the ZITADEL API as Authorization Header.
|
||||
In this example we read the organization of the service user.
|
||||
|
||||
```bash
|
||||
curl --request GET \
|
||||
--url {your-domain}/management/v1/orgs/me \
|
||||
--header 'Authorization: Bearer ${TOKEN}'
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
* With service users you can secure machine-to-machine communication
|
||||
* Client Credentials provide an alternative way to JWT Profile for service user authentication
|
||||
* After successful authorization you can use an access token like for human users
|
||||
|
||||
Where to go from here:
|
||||
|
||||
* Management API
|
||||
* Securing backend API
|
@@ -109,6 +109,7 @@ module.exports = {
|
||||
items: [
|
||||
"guides/integrate/serviceusers",
|
||||
"guides/integrate/access-zitadel-apis",
|
||||
"guides/integrate/client-credentials",
|
||||
"guides/integrate/pat",
|
||||
"guides/integrate/access-zitadel-system-api",
|
||||
"guides/integrate/export-and-import",
|
||||
|
BIN
docs/static/img/console_serviceusers_secret.gif
vendored
Normal file
BIN
docs/static/img/console_serviceusers_secret.gif
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 MiB |
Reference in New Issue
Block a user