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:
Stefan Benz
2023-01-31 20:52:47 +01:00
committed by GitHub
parent 7c7c93117b
commit e2fdd3f077
48 changed files with 2113 additions and 311 deletions

View File

@@ -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 |

View File

@@ -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)

View File

@@ -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

View File

@@ -133,6 +133,7 @@ title: zitadel/user.proto
| ----- | ---- | ----------- | ----------- |
| name | string | - | |
| description | string | - | |
| has_secret | bool | - | |

View 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.
:::
![Create new service user](/img/console_serviceusers_secret.gif)
## 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))
![Add org owner to service user](/img/guides/console-service-user-org-owner.gif)
## 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

View File

@@ -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",

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB