212 lines
7.5 KiB
Markdown
Raw Normal View History

---
title: Service Users
---
<table class="table-wrapper">
<tr>
<td>Description</td>
<td>Learn the basics about ZITADEL Service Users, how to set them up and authorize with ZITADEL.</td>
</tr>
<tr>
<td>Learning Outcomes</td>
<td>
In this module you will:
<ul>
<li>Learn about Service Users</li>
<li>Create a Service User in ZITADEL Console</li>
<li>Authorize a Service User with JWT signed with your private key</li>
</ul>
</td>
</tr>
<tr>
<td>Prerequisites</td>
<td>
<ul>
docs: restructure and add more content (#2232) * docs: restructure quickstarts * docs: quickstart libs * docs: add exlixir lib * docs: restructure * docs: restructure * docs: customization * use tabs in oauth/oidc endpoints.md * improve authorize_endpoint * improve token_endpoint and introspection_endpoint * docs: saas use case * styling * styling * begin login users * login users * login users * fix table * fix: links * fix: links * start go api * fix: policy structure * fix: saas description * fix: saas description * fix: saas description * Update docs/docs/concepts/policies.md Co-authored-by: mffap <mpa@caos.ch> * Update docs/docs/concepts/usecases/saas.md Co-authored-by: mffap <mpa@caos.ch> * Update docs/docs/concepts/usecases/saas.md Co-authored-by: mffap <mpa@caos.ch> * Update docs/docs/concepts/usecases/saas.md Co-authored-by: mffap <mpa@caos.ch> * Update docs/docs/concepts/usecases/saas.md Co-authored-by: mffap <mpa@caos.ch> * Update docs/docs/concepts/usecases/saas.md Co-authored-by: mffap <mpa@caos.ch> * Update docs/docs/concepts/usecases/saas.md Co-authored-by: mffap <mpa@caos.ch> * Update docs/docs/concepts/usecases/saas.md Co-authored-by: mffap <mpa@caos.ch> * Update docs/docs/concepts/usecases/saas.md Co-authored-by: mffap <mpa@caos.ch> * Update docs/docs/concepts/usecases/saas.md Co-authored-by: mffap <mpa@caos.ch> * docs: organisation --> organization * docs: fixes * docs: fixes * Update docs/docs/apis/openidoauth/endpoints.md Co-authored-by: Florian Forster <florian@caos.ch> * Update docs/docs/concepts/policies.md Co-authored-by: Florian Forster <florian@caos.ch> * Update docs/docs/concepts/policies.md Co-authored-by: Florian Forster <florian@caos.ch> * Update docs/docs/concepts/policies.md Co-authored-by: Florian Forster <florian@caos.ch> * Update docs/docs/apis/openidoauth/endpoints.md Co-authored-by: Florian Forster <florian@caos.ch> * Apply suggestions from code review Co-authored-by: Florian Forster <florian@caos.ch> * docs: intro * Update docs/docs/guides/customization/branding.md Co-authored-by: Florian Forster <florian@caos.ch> * fix: typo * add mermaid Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: mffap <mpa@caos.ch> Co-authored-by: Florian Forster <florian@caos.ch>
2021-09-23 10:34:06 +02:00
<li>Knowledge of <a href="/docs/guides/authorization/oauth-recommended-flows">Recommended Authorization Flows</a></li>
</ul>
</td>
</tr>
</table>
## Human vs. Machine
ZITADEL supports human an machine users. We call human users simply "Users" and machine users "Service Users".
With Service Users you would typically secure backend services. For example in ZITADEL you would require an authenticated Service User to access the Management API. The main difference between human and machine users is the type of credentials that can be used for authentication: Human users typically logon via an login prompt, but Machine users require a non-interactive logon process.
## Exercise: Create a Service User
1. Navigate to Service Users
2. Click on **New**
3. Enter a user name and a display name
![Create new service user](/img/console_serviceusers_create.gif)
## Authenticating a service user
In ZITADEL we use the `private_jwt` (**“JWT bearer token with private key”**, [RFC7523](https://tools.ietf.org/html/rfc7523)) authorization grant for this non-interactive authentication.
You need to follow these steps to authenticate a service user and receive a access token:
1. Generate a private-public key pair in ZITADEL
2. Create a JSON Web Token (JWT) and sign with private key
3. With this JWT, request an OAuth token from ZITADEL
With this token you can make subsequent requests, just like a human user.
## Exercise: Get an access token
In this exercise we will authenticate a service user and receive an access_token to use against a API.
> **Information:** Are you stuck? Don't hesitate to reach out to us on [Github Discussions](https://github.com/caos/zitadel/discussions) or [contact us](https://zitadel.ch/contact/) privately.
### 1. Generate a private-public key pair in ZITADEL
Select your service user and in the section KEYS click **New**. Enter an expiration date and click **Add**. Make sure to download the json by clicking **Download**.
![Create private key](/img/console_serviceusers_new_key.gif)
The downloaded json should look something like outlined below. The value of `key` contains the *private* key for your service account. Please make sure to keep this key securely stored and handle with care. The public key is automatically stored in ZITADEL.
```json
{
"type":"serviceaccount",
"keyId":"100509901696068329",
"key":"-----BEGIN RSA PRIVATE KEY----- [...] -----END RSA PRIVATE KEY-----\n",
"userId":"100507859606888466"
}
```
### 2. Create a JWT and sign with private key
You need to create a JWT with the following header and payload and sign it with the RS256 algorithm.
Header
```json
{
"alg": "RS256",
"kid":"100509901696068329"
}
```
Make sure to include `kid` in the header with the value of `keyId` from the downloaded JSON.
Payload
```json
{
"iss": "100507859606888466",
"sub": "100507859606888466",
"aud": "https://issuer.zitadel.ch",
"iat": [Current UTC timestamp, e.g. 1605179982],
"exp": [UTC timestamp, max. 1 hour from iat, e.g. 1605183582]
}
```
* `iss` represents the requesting party, i.e. the owner of the private key. In this case the value of `userId` from the downloaded JSON.
* `sub` represents the application. Set the value also to the value of `userId`
* `aud` must be ZITADEL's issuing domain
* `iat` is a unix timestamp of the creation signing time of the JWT, e.g. now
* `exp` is the unix timestamp of expiry of this assertion. Must be less than 1 hour from `iat`
feat(operator): make running ZITADEL easy (#1562) * docs: describe crd mode * docs: fix links * docs: fix commands and crdb resources * feat: add configure command * chore: use latest ORBOS * chore: use latest ORBOS * docs: start gitops docs * fix: compile * chore: fix build script path * chore: remove redundant prebuild * chore: add configure.go * docs: describe gitops mode * docs: point template links to main branch * docs: fix versions * feat: initialize empty keys * feat: reconfigure running ZITADEL * docs: describe crd mode * docs: fix links * docs: fix commands and crdb resources * feat: add configure command * chore: use latest ORBOS * chore: use latest ORBOS * docs: start gitops docs * fix: compile * chore: fix build script path * chore: remove redundant prebuild * chore: add configure.go * docs: describe gitops mode * docs: point template links to main branch * docs: fix versions * feat: initialize empty keys * feat: reconfigure running ZITADEL * test: fix * docs: keys are generated with configure * docs: remove keys from template * chore: pass compile time data * chore: use latest ORBOS * fix: when in-cluster, use in-cluster k8s client * fix: try in-cluster config if kubeconfig is empty * fix: reduce unneeded side effects for configure command * docs: boom version * chore: use latest ORBOS * chore: use latest ORBOS * initial commit * inital changes * commit WIP Information Architecture * commit a working state * add static assets and project * add org and fix img names * add plausible * remove img * change sidebar to easier mgmt * add openid oauth and domains * lint md * quickstarts * add auth flow * identity brokering * remove site * fix broken links * extend footer * extend readme * fix: styling * fix: zitadel logo on index * styling * border * fix: nav * fix: nav * fix: index * fix: corrected zitadelctl examples * fix: rename architecture to concepts * fix: introductions * fix: introductions * fix: introductions * docs: cli r/w secrets examples * docs: finish ZITADEL Enterprise Cloud * docs: mention ZITADEL Enterprise Cloud tier * docs: comment configuration options * docs: fix broken links * docs: move some introduction texts around * docs: twilio and email are mandatory * docs: download latest binaries Co-authored-by: Florian Forster <florian@caos.ch> Co-authored-by: fabi <fabienne.gerschwiler@gmail.com> Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: Stefan Benz <stefan@caos.ch>
2021-04-22 18:43:34 +02:00
Please refer to [JWT_with_Private_Key](../../apis/openidoauth/authn-methods#jwt-with-private-key) in the documentation for further information.
> **Information:** The `exp` claim is currently not validated, but will be with a future release. Make sure that `exp` is less than 1 hour starting from `iat`.
If you use Go, you might want to use the [provided tool](https://github.com/caos/zitadel-tools) to generate a JWT from the downloaded json. There are many [libraries](https://jwt.io/#libraries-io) to generate and sign JWT.
### 3. With this JWT, request an OAuth token from ZITADEL
With the encoded JWT from the prior step, you will need to craft a POST request to ZITADEL's token endpoint:
```bash
curl --request POST \
--url https://api.zitadel.ch/oauth/v2/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
--data scope='openid profile email' \
--data assertion=eyJ0eXAiOiJKV1QiL...
```
* `grant_type` should be set to `urn:ietf:params:oauth:grant-type:jwt-bearer`
feat(operator): make running ZITADEL easy (#1562) * docs: describe crd mode * docs: fix links * docs: fix commands and crdb resources * feat: add configure command * chore: use latest ORBOS * chore: use latest ORBOS * docs: start gitops docs * fix: compile * chore: fix build script path * chore: remove redundant prebuild * chore: add configure.go * docs: describe gitops mode * docs: point template links to main branch * docs: fix versions * feat: initialize empty keys * feat: reconfigure running ZITADEL * docs: describe crd mode * docs: fix links * docs: fix commands and crdb resources * feat: add configure command * chore: use latest ORBOS * chore: use latest ORBOS * docs: start gitops docs * fix: compile * chore: fix build script path * chore: remove redundant prebuild * chore: add configure.go * docs: describe gitops mode * docs: point template links to main branch * docs: fix versions * feat: initialize empty keys * feat: reconfigure running ZITADEL * test: fix * docs: keys are generated with configure * docs: remove keys from template * chore: pass compile time data * chore: use latest ORBOS * fix: when in-cluster, use in-cluster k8s client * fix: try in-cluster config if kubeconfig is empty * fix: reduce unneeded side effects for configure command * docs: boom version * chore: use latest ORBOS * chore: use latest ORBOS * initial commit * inital changes * commit WIP Information Architecture * commit a working state * add static assets and project * add org and fix img names * add plausible * remove img * change sidebar to easier mgmt * add openid oauth and domains * lint md * quickstarts * add auth flow * identity brokering * remove site * fix broken links * extend footer * extend readme * fix: styling * fix: zitadel logo on index * styling * border * fix: nav * fix: nav * fix: index * fix: corrected zitadelctl examples * fix: rename architecture to concepts * fix: introductions * fix: introductions * fix: introductions * docs: cli r/w secrets examples * docs: finish ZITADEL Enterprise Cloud * docs: mention ZITADEL Enterprise Cloud tier * docs: comment configuration options * docs: fix broken links * docs: move some introduction texts around * docs: twilio and email are mandatory * docs: download latest binaries Co-authored-by: Florian Forster <florian@caos.ch> Co-authored-by: fabi <fabienne.gerschwiler@gmail.com> Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: Stefan Benz <stefan@caos.ch>
2021-04-22 18:43:34 +02:00
* `scope` should contain any [Scopes](../../apis/openidoauth/scopes) you want to include, but must include `openid`. For this example, please include `profile` and `email`
* `assertion` is the encoded value of the JWT that was signed with your private key from the prior step
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
}
```
### 4. Verify that you have a valid access token
For this example let's call the userinfo endpoint to verfiy that our access token works.
```bash
curl --request POST \
--url https://api.zitadel.ch/oauth/v2/userinfo \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer MtjHodGy4zxKylDOhg6kW90WeEQs2q...'
```
You should receive a response with your service user's information.
```bash
HTTP/1.1 200 OK
Content-Type: application/json
{
"name": "MyServiceUser",
"preferred_username": "service_user@acme.zitadel.ch",
"updated_at": 1616417938
}
```
## Knowledge Check
* To secure backend APIs you can request an API key via ZITADEL's console
- [ ] yes
- [ ] no
* The JWT header must contain a property `kid` with the value of the key ID
- [ ] yes
- [ ] no
* After generating a key for your service user, you must download the public key and sign your JWT with the public key
- [ ] yes
- [ ] no
<details>
<summary>
Solutions
</summary>
* To secure backend APIs you can request an API key via ZITADEL's console
- [ ] yes
- [x] no (We use **“JWT bearer token with private key”**, [RFC7523](https://tools.ietf.org/html/rfc7523))
* The JWT header must contain a property `kid` with the value of the key ID
- [x] yes
- [ ] no
* After generating a key for your service user, you must download the public key and sign your JWT with the public key
- [ ] yes
- [x] no (The json file contains the private key. Handle with care.)
</details>
## Summary
* With service users you can secure machine-to-machine communication
* Because there is no interactive logon, you need to use a JWT signed with your private key to authorize the user
* After successful authorization you can use an access token like for human users
Where to go from here:
* Management API
* Securing backend API