mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
# Which Problems Are Solved The docs ([Actions v2 concepts page](https://zitadel.com/docs/concepts/features/actions_v2) and the Actions testing guides) state that Actions v2 must be enabled via the "Actions" feature flag. That flag no longer exists — it is `reserved` in `proto/zitadel/feature/v2/instance.proto` and `system.proto` — and Actions v2 is available by default. The callout even linked to a non-existent API field. # How the Problems Are Solved - Removed the stale feature-flag callout (and the roadmap teaser callout) from `concepts/features/actions_v2.mdx`. - Removed the "Your ZITADEL instance needs to have the actions feature enabled." prerequisite bullet from the seven `guides/integrate/actions/testing-*.mdx` guides. Pure deletions, 17 lines across 8 files. # Additional Context The `SetInstanceFeatures` mentions in `hosted-login.mdx` and `webkeys.mdx` refer to flags that still exist and are untouched.
243 lines
7.5 KiB
Plaintext
243 lines
7.5 KiB
Plaintext
---
|
|
title: Test Actions Request Manipulation
|
|
description: "Create an HTTP server as a target to receive and manipulate API requests using ZITADEL's actions feature for custom integration"
|
|
---
|
|
|
|
This guide shows you how to leverage the ZITADEL actions feature to manipulate API requests in your ZITADEL instance.
|
|
You can use the actions feature to create a target that will be called when a specific API request occurs.
|
|
This is useful for adding information to managed resources in ZITADEL.
|
|
|
|
## 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>
|
|
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>
|
|
|
|
<Callout>
|
|
To marshal and unmarshal the request please use a package like [protojson](https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson),
|
|
as the request is a protocol buffer message, to avoid potential problems with the attribute names.
|
|
</Callout>
|
|
|
|
## 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, process the request and returns the manipulated request.
|
|
For this example, we will use a simple Go HTTP server that will return the request with added metadata.
|
|
|
|
<Callout>
|
|
The signature of the received request can be checked, [please refer to the example for more information on how to](/guides/integrate/actions/testing-request-signature).
|
|
</Callout>
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/zitadel/zitadel-go/v3/pkg/client/zitadel/user/v2"
|
|
"google.golang.org/protobuf/encoding/protojson"
|
|
)
|
|
|
|
type contextRequest struct {
|
|
Request *createUserRequestWrapper `json:"request"`
|
|
}
|
|
|
|
// createUserRequestWrapper necessary to marshal and unmarshal the JSON into the proto message correctly
|
|
type createUserRequestWrapper struct {
|
|
user.CreateUserRequest
|
|
}
|
|
|
|
func (r *createUserRequestWrapper) MarshalJSON() ([]byte, error) {
|
|
data, err := protojson.Marshal(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func (r *createUserRequestWrapper) UnmarshalJSON(data []byte) error {
|
|
return protojson.Unmarshal(data, r)
|
|
}
|
|
|
|
// call HandleFunc to read the request body, manipulate the content and return the manipulated request
|
|
func call(w http.ResponseWriter, req *http.Request) {
|
|
// read the body content
|
|
sentBody, err := io.ReadAll(req.Body)
|
|
if err != nil {
|
|
// if there was an error while reading the body return an error
|
|
http.Error(w, "error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer req.Body.Close()
|
|
|
|
// read the request into the expected structure
|
|
request := new(contextRequest)
|
|
if err := json.Unmarshal(sentBody, request); err != nil {
|
|
http.Error(w, "error", http.StatusInternalServerError)
|
|
}
|
|
|
|
// build the response from the received request
|
|
response := request.Request
|
|
// manipulate the request to send back as response
|
|
metadata := response.GetHuman().GetMetadata()
|
|
if metadata == nil {
|
|
metadata = make([]*user.Metadata, 0)
|
|
}
|
|
metadata = append(metadata, &user.Metadata{Key: "organization", Value: []byte("dunder mifflin")})
|
|
|
|
response.GetHuman().Metadata = metadata
|
|
|
|
// marshal the request into json
|
|
data, err := json.Marshal(response)
|
|
if err != nil {
|
|
// if there was an error while marshalling the json
|
|
http.Error(w, "error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// return the manipulated request
|
|
w.Write(data)
|
|
}
|
|
|
|
func main() {
|
|
// handle the HTTP call under "/call"
|
|
http.HandleFunc("/call", call)
|
|
|
|
// start an HTTP server with the before defined function to handle the endpoint under "http://localhost:8090"
|
|
http.ListenAndServe(":8090", nil)
|
|
}
|
|
|
|
```
|
|
|
|
<Callout title="">
|
|
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>
|
|
|
|
## Create target
|
|
|
|
As you see in the example above the target is created with HTTP and port '8090' and if we want to use it as call, the target can be created as follows:
|
|
|
|
See [Create a target](/reference/api/action/zitadel.action.v2.ActionService.CreateTarget) for more detailed information.
|
|
|
|
```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 call",
|
|
"restCall": {
|
|
"interruptOnError": true
|
|
},
|
|
"endpoint": "http://localhost:8090/call",
|
|
"timeout": "10s"
|
|
}'
|
|
```
|
|
|
|
Save the returned ID to set in the execution.
|
|
|
|
## Set execution
|
|
|
|
To call the target just created before, with the intention to manipulate the request used for user creation by the user V2 API, we define an execution with a method condition.
|
|
|
|
See [Set an execution](/reference/api/action/zitadel.action.v2.ActionService.SetExecution) 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>"
|
|
]
|
|
}'
|
|
```
|
|
|
|
## 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 manipulate the request to something like the following. Check out
|
|
the [Sent information Request](./usage#sent-information-request) payload description.
|
|
|
|
```shell
|
|
curl -L -X PUT '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]"
|
|
},
|
|
"metadata":
|
|
[
|
|
{
|
|
"key": "organization",
|
|
"value": "ZHVuZGVyIG1pZmZsaW4="
|
|
}
|
|
]
|
|
}
|
|
}'
|
|
```
|
|
|
|
## Conclusion
|
|
|
|
You have successfully set up a target and execution to manipulate API requests in your ZITADEL instance.
|
|
This feature can now be used to add or manipulate information to managed resources in ZITADEL.
|
|
Find more information about the actions feature in the [API documentation](/concepts/features/actions_v2).
|