mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
## Problem statement - `<Tabs>` were not rendered in docs - Missing steps in README for local dev ## How it was solved - Enhance the README to better describe local development setup and clean up formatting. - Standardize the use of tabs across various components for consistency. ## Additional info To not cause merge conflicts with https://github.com/zitadel/zitadel/pull/11729 changes in `/self-hosting/manage/configure/configure.mdx` were skipped
242 lines
8.7 KiB
Plaintext
242 lines
8.7 KiB
Plaintext
---
|
|
title: Verify Payload Integrity
|
|
description: "Validate payload integrity by verifying JSON, JWT, or JWE signatures when receiving data from ZITADEL actions"
|
|
---
|
|
|
|
import JsonGo from './go/json.go';
|
|
import JwtGo from './go/jwt.go';
|
|
import JweGo from './go/jwe.go';
|
|
import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock';
|
|
|
|
This guide shows you how to verify the integrity of the received data on your target.
|
|
There are three options available, which will be demonstrated in the following sections.
|
|
The examples are based on the request action, but the same principles apply to other action types as well.
|
|
|
|
## Prerequisites
|
|
|
|
Before you start, make sure you have everything set up correctly.
|
|
|
|
- You need to be at least a ZITADEL [_IAM_OWNER_](/guides/manage/console/administrators)
|
|
|
|
<Callout type="info">
|
|
Note that this guide assumes that ZITADEL is running on the same machine as the target and can be reached via `localhost`.
|
|
In case you are using a different setup, you need to adjust the target URL accordingly and will need to make sure that the target is reachable from ZITADEL.
|
|
</Callout>
|
|
|
|
## Payload and Validation Types
|
|
|
|
ZITADEL supports the following three types of payload in actions:
|
|
- **JSON**: The payload is sent as JSON in the request body. Additionally, a signature header is sent to validate the payload.
|
|
This is the simplest form of payload and validation and doesn't require any additional calls to validate the payload. It's also the default type,
|
|
which will be used if no type is specified.
|
|
- **JWT**: The payload is sent as a JSON Web Token (JWT) in the request body. The JWT is signed with the signing key of the instance.
|
|
This allows you to validate the payload by verifying the signature of the JWT using the signing key managed though the webkeys endpoint of the ZITADEL instance,
|
|
which allows for easier key rotation and management. Additionally, it's the base for the JWE type, in case you need to encrypt the payload for certain use cases.
|
|
- **JWE**: The payload is sent as encrypted JSON Web Token (JWE) in the request body. The JWT is additionally encrypted with the public key provided by you.
|
|
This allows you to validate and decrypt the payload by verifying the signature of the JWT using the signing key managed though the webkeys endpoint of the ZITADEL instance,
|
|
and decrypting the payload using your private key. This is the most secure form of payload and validation, but requires additional setup to provide the public key to ZITADEL.
|
|
This type is recommended if the payload contains sensitive information that should not be exposed to any intermediaries.
|
|
|
|
## Create target
|
|
|
|
We'll start the endpoint on port '8090' and if we want to use it as webhook, the target can be created as follows:
|
|
|
|
See [Create a target](/apis/resources/action_service_v2/action-service-create-target) for more detailed information.
|
|
|
|
Specify the `payloadType` according to the implementation you want to test: `PAYLOAD_TYPE_JSON`, `PAYLOAD_TYPE_JWT`, or `PAYLOAD_TYPE_JWE`.
|
|
|
|
```shell
|
|
curl -L -X POST 'https://${CUSTOM_DOMAIN}/v2/actions/targets' \
|
|
-H 'Content-Type: application/json' \
|
|
-H 'Accept: application/json' \
|
|
-H 'Authorization: Bearer <TOKEN>' \
|
|
--data-raw '{
|
|
"name": "local webhook",
|
|
"restWebhook": {
|
|
"interruptOnError": true
|
|
},
|
|
"endpoint": "http://localhost:8090/webhook",
|
|
"timeout": "10s",
|
|
"payloadType": "PAYLOAD_TYPE_JSON"
|
|
}'
|
|
```
|
|
|
|
Example response after creating the target:
|
|
```json
|
|
{
|
|
"id": "344649040681500814",
|
|
"creationDate": "2025-10-31T15:00:36.432595dZ",
|
|
"signingKey": "somekey"
|
|
}
|
|
```
|
|
Save the returned ID to set in the execution. If you're intending to use the `PAYLOAD_TYPE_JSON`, additionally store the `signingKey` and use it in the example above.
|
|
|
|
### JWE Specific Setup
|
|
|
|
If you are using the `PAYLOAD_TYPE_JWE`, you need to provide a public key to ZITADEL so that it can encrypt the payload.
|
|
|
|
Create a public/private key pair. You can use the following command to generate an RSA key pair:
|
|
```shell
|
|
openssl genpkey -algorithm RSA -outform PEM -out private_key.pem -pkeyopt rsa_keygen_bits:2048
|
|
openssl rsa -pubout -in private_key.pem -out public_key.pem
|
|
```
|
|
|
|
Then upload the public key to ZITADEL using the following command. Replace `<TargetID>` with the ID of the target you created earlier.
|
|
Use the base64 encoded content of the `public_key.pem` file as the value for `publicKey`.
|
|
```shell
|
|
curl -L -X POST 'https://${CUSTOM_DOMAIN}/v2/actions/targets/<TargetID>/publickeys' \
|
|
-H 'Content-Type: application/json' \
|
|
-H 'Accept: application/json' \
|
|
-H 'Authorization: Bearer <TOKEN>' \
|
|
--data-raw '{
|
|
"publicKey": "<base64 encoded contents of public_key.pem>"
|
|
}'
|
|
```
|
|
|
|
Be sure to also activate the public key for the target using the returned `<KeyID>` from the previous request:
|
|
```shell
|
|
curl -L -X PUT 'https://${CUSTOM_DOMAIN}/v2/actions/targets/<TargetID>/publickeys/<KeyID>/activate' \
|
|
-H 'Content-Type: application/json' \
|
|
-H 'Accept: application/json' \
|
|
-H 'Authorization: Bearer <TOKEN>'
|
|
```
|
|
|
|
## Set execution
|
|
|
|
To configure ZITADEL to call the target when an API endpoint is called, you need to set an execution and define the request
|
|
condition.
|
|
|
|
See [Set an execution](/apis/resources/action_service_v2/action-service-set-execution) for more detailed information.
|
|
|
|
```shell
|
|
curl -L -X PUT 'https://${CUSTOM_DOMAIN}/v2/actions/executions' \
|
|
-H 'Content-Type: application/json' \
|
|
-H 'Accept: application/json' \
|
|
-H 'Authorization: Bearer <TOKEN>' \
|
|
--data-raw '{
|
|
"condition": {
|
|
"request": {
|
|
"method": "/zitadel.user.v2.UserService/CreateUser"
|
|
}
|
|
},
|
|
"targets": [
|
|
"<TargetID returned>"
|
|
]
|
|
}'
|
|
```
|
|
|
|
## Start example target
|
|
|
|
To test the actions feature, you need to create a target that will be called when an API endpoint is called.
|
|
You will need to implement a listener that can receive HTTP requests.
|
|
For this example, we will use a simple Go HTTP server that will print the received request to standard output.
|
|
As mentioned before, this validation can and should be applied to any target implementation.
|
|
|
|
<Tabs defaultIndex={0} items={['JSON', 'JWT', 'JWE']}>
|
|
<Tab value="JSON">
|
|
<DynamicCodeBlock lang="go" code={JsonGo} />
|
|
</Tab>
|
|
<Tab value="JWT">
|
|
<DynamicCodeBlock lang="go" code={JwtGo} />
|
|
</Tab>
|
|
<Tab value="JWE">
|
|
<DynamicCodeBlock lang="go" code={JweGo} />
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
<Callout type="info">
|
|
The example above runs only on your local machine (`localhost`).
|
|
To test it with Zitadel, you must make your listener reachable from the internet.
|
|
You can do this by using **Webhook.site** (see [Creating a Listener with Webhook.site](./webhook-site-setup)).
|
|
</Callout>
|
|
|
|
## Example call
|
|
|
|
Now that you have set up the target and execution, you can test it by creating a user through the Management Console UI or
|
|
by calling the ZITADEL API to create a user.
|
|
|
|
```shell
|
|
curl -L -X POST 'https://${CUSTOM_DOMAIN}/v2/users/new' \
|
|
-H 'Content-Type: application/json' \
|
|
-H 'Accept: application/json' \
|
|
-H 'Authorization: Bearer <TOKEN>' \
|
|
--data-raw '{
|
|
"organizationId": "344648897353810062",
|
|
"human":
|
|
{
|
|
"profile":
|
|
{
|
|
"givenName": "Minnie",
|
|
"familyName": "Mouse",
|
|
"nickName": "Mini",
|
|
"displayName": "Minnie Mouse",
|
|
"preferredLanguage": "en",
|
|
"gender": "GENDER_FEMALE"
|
|
},
|
|
"email":
|
|
{
|
|
"email": "[email protected]"
|
|
}
|
|
}
|
|
}'
|
|
```
|
|
|
|
Your server should now print out something like the following. Check out
|
|
the [Sent information Request](./usage#sent-information-request) payload description.
|
|
|
|
```json
|
|
{
|
|
"fullMethod": "/zitadel.user.v2.UserService/CreateUser",
|
|
"instanceID": "344648897353744526",
|
|
"orgID": "344648897353810062",
|
|
"projectID": "344648897353875598",
|
|
"userID": "344648897354465422",
|
|
"request":
|
|
{
|
|
"organizationId": "344648897353810062",
|
|
"human":
|
|
{
|
|
"profile":
|
|
{
|
|
"givenName": "Minnie",
|
|
"familyName": "Mouse",
|
|
"nickName": "Mini",
|
|
"displayName": "Minnie Mouse",
|
|
"preferredLanguage": "en",
|
|
"gender": "GENDER_FEMALE"
|
|
},
|
|
"email":
|
|
{
|
|
"email": "[email protected]"
|
|
}
|
|
}
|
|
},
|
|
"headers":
|
|
{
|
|
"Content-Type":
|
|
[
|
|
"application/grpc"
|
|
],
|
|
"Host":
|
|
[
|
|
"localhost:8080"
|
|
],
|
|
"X-Forwarded-For":
|
|
[
|
|
"::1"
|
|
],
|
|
"X-Forwarded-Host":
|
|
[
|
|
"localhost:8080"
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
## Conclusion
|
|
|
|
You have successfully set up a target and verified the payload integrity for request actions using your preferred payload type.
|
|
You can now extend this setup to other action types and integrate it into your workflows as needed.
|
|
Selecting the appropriate payload type ensures that your data is transmitted securely and can be validated effectively on the receiving end.
|
|
Find more information about the actions feature in the [API documentation](/concepts/features/actions_v2).
|