---
title: Extend Authorization in ZITADEL with Organization Metadata + preAccessToken Action
description: Extend ZITADEL auth by mapping roles to permissions via Organization metadata and a preAccessToken Action that appends a permissions claim.
---

[Skip to content](https://help.zitadel.com/extend-authorization-in-zitadel-with-organization-metadata-preaccesstoken-action-#main-content)

[![zitadel-logo\_text-dark (1)](https://help.zitadel.com/hs-fs/hubfs/zitadel-logo_text-dark%20(1).png?width=210&height=60&name=zitadel-logo_text-dark%20(1).png)](https://help.zitadel.com/?hsLang=en)

Open main navigation

Close main navigation

- [Documentation](https://zitadel.com/docs)

[Documentation](https://zitadel.com/docs)

 How can we help you?

- There are no suggestions because the search field is empty.

1. [ZITADEL Knowledge Base](https://help.zitadel.com/?hsLang=en)
2. [Authentication & Login](https://help.zitadel.com/authentication-login?hsLang=en)

November 10, 2025

# Extend Authorization in ZITADEL with Organization Metadata + preAccessToken Action

## Map roles to permissions in your org metadata and append a permissions claim to Access Tokens during the pre-issuance flow.

This guide shows how to enrich Access Tokens with a permissions claim derived from your Organization’s role-to-permissions mapping. You’ll store that mapping in Organization metadata, verify ZITADEL’s webhook signature, read the metadata inside a **Function → preAccessToken** Action, and return append\_claims so the permissions array is issued with every token. We include example metadata shapes, sample responses, and UI/API steps.

**Reference implementation:** Check our [Actions V2 + Cloudflare Workers utility](https://github.com/zitadel/actions/tree/main/actions-v2-cloudflare-workers) and read the **AUTHORIZATION.md** guide. It exposes an endpoint to handle role-to-permissions mapping.

#### **Why this pattern?**

Many apps authorize on **permissions**, while ZITADEL natively grants **roles** on projects/grants. By adding a lightweight Action in the **pre access token creation** flow, you can translate roles → permissions at token time and deliver a standard `permissions` claim your API gateways and services already understand. This runs just before token issuance in the **PreAccessToken Flow**.

#### **Prerequisites**

- **ZITADEL v3+** with **Actions v2** enabled on your instance.
- A service user (PAT or Client Credentials) with access to **Organization Service v2** to read metadata. (See [API reference](https://zitadel.com/docs/category/apis/resources/org_service_v2/organization-service)).
- A project with users who already hold **project roles** via user grants.

#### **Step 1 — Model your permissions in Organization Metadata**

You’ll store a mapping from **role name** → **array of permissions** in Organization metadata. ZITADEL’s v2 Organization API exposes **ListOrganizationMetadata** / **SetOrganizationMetadata**.

##### **Recommended shape: Per-role keys (simple & scalable)**

Create one metadata entry per role, where the key equals the **role name**:

- Key: admin → Value (JSON string): `["read:all","write:all","delete:all"]`
- Key: editor → `["posts:read","posts:write"]`
- Key: viewer → `["posts:read"]`

This keeps updates surgical (change one role value, no collisions) and matches the logic “for each user role, look up a same-named metadata key”.

##### **Where to set it**

- **Console**: Organization → **Metadata** (paste JSON strings in the Value field).

![IMG\_4440](https://help.zitadel.com/hs-fs/hubfs/IMG_4440.png?width=670&height=278&name=IMG_4440.png)

- **API v2**: Organization Service → [**SetOrganizationMetadata**](https://zitadel.com/docs/apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-set-organization-metadata).

#### **Step 2 — Create a Target (webhook) and capture the signing key**

Create a **Target** that ZITADEL will call from the Action. The response includes a **signingKey** that you must use to verify the request signature. Example ([docs snippet](https://zitadel.com/docs/guides/integrate/actions/testing-request-signature#create-target)):

```
curl -L -X POST "https://$CUSTOM_DOMAIN/v2/actions/targets" \  -H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \  --data-raw '{    "name": "Authorization Webhook",    "restCall": { "interruptOnError": false },    "endpoint": "https://<HOSTING_DOMAIN>/",    "timeout": "10s"  }'# Response (excerpt)# { "id": "342320645008366333", "signingKey": "<SIGNING_KEY>" }
```

 

Keep that signingKey safe; this must be saved as the `AUTHORIZATION_SIGNING_KEY` environment variable. See the [Actions V2 + Cloudflare Workers utility](https://github.com/zitadel/actions/tree/main/actions-v2-cloudflare-workers) and read the **AUTHORIZATION.md** guide for more details.

#### **Step 3 — Implement the preAccessToken Action (Function)**

This Action runs in the **Function execution → preAccessToken condition** and can **append claims** by returning an `append_claims` array. 

##### **What the sample code will do**

1. **Verify signature** using the `signingKey`.
2. **Parse request** and collect the user’s **roles** (e.g., from `user_grants[].roles`) supplied in the preAccessToken payload.
3. **Fetch Organization metadata** via **ListOrganizationMetadata** (v2).
4. **Map roles → permissions** by reading a metadata entry whose **key equals the role name**.
5. **Return** an `append_claims` object with a **deduplicated** `permissions` array.

##### **Minimal example response**

```
{  "append_claims": [    {      "key": "permissions",      "value": ["read:all", "write:all"]    }  ]}
```

 

That’s all that’s required for ZITADEL to add permissions into the Access Token. 

#### **Step 4 — Wire it up in Console**

1. Go to **Console → Actions**.
2. **Create Function** → select **pre access token creation**.
3. Choose your **Authorization Webhook** Target and save.

#### **Step 5 — Test and inspect the token**

 Use any OIDC client (or ZITADEL’s **OIDC Playground**) to request an Access Token and then decode the JWT to verify the claim. A minimal vanilla JS app can be used to login and decode the user tokens to demonstrate the final result:

- [Zitadel JS Quickstart](https://github.com/zitadel/zitadel-vanilla-js)

##### **Example decoded Access Token payload (excerpt)**

```
{  "iss": "https://auth.example.com",  "aud": [    "259256588127174658",    "257786991247294468"  ],  "sub": "259242039378444290",  "permissions": ["read:all", "write:all", "delete:all"],  "exp": 1762730441,  "iat": 1762726841}
```

 

- [Getting Started](https://help.zitadel.com/getting-started?hsLang=en)
- [User Management](https://help.zitadel.com/user-management?hsLang=en)
- [Authentication & Login](https://help.zitadel.com/authentication-login?hsLang=en)
- [Security & Compliance](https://help.zitadel.com/security-compliance?hsLang=en)
- [Troubleshooting](https://help.zitadel.com/troubleshooting?hsLang=en)
- [Account & Subscription](https://help.zitadel.com/account-subscription?hsLang=en#main-content)

    - [Support](https://help.zitadel.com/account-subscription?hsLang=en#support)
    - [Billing (Pro)](https://help.zitadel.com/account-subscription?hsLang=en#billing-pro)
    - [Billing (Enterprise)](https://help.zitadel.com/account-subscription?hsLang=en#billing-enterprise)
    - [Zitadel Cloud Account](https://help.zitadel.com/account-subscription?hsLang=en#zitadel-cloud-account)
    - [Service Description](https://help.zitadel.com/account-subscription?hsLang=en#service-description)
- [Default Settings](https://help.zitadel.com/default-settings?hsLang=en#main-content)

    - [Web Keys](https://help.zitadel.com/default-settings?hsLang=en#web-keys)
- [Self-Hosting](https://help.zitadel.com/self-hosting?hsLang=en)

[![zitadel-logo\_text-dark](https://help.zitadel.com/hs-fs/hubfs/zitadel-logo_text-dark.png?width=210&height=60&name=zitadel-logo_text-dark.png "zitadel-logo_text-dark")](http://zitadel.com)

1 Embarcadero Center, Suite 1200  
San Francisco, CA 94111

<https://github.com/zitadel/zitadel> <https://x.com/zitadel> <https://www.facebook.com/zitadel.social> <https://www.linkedin.com/company/zitadel/> <https://zitadel.com/chat> <https://www.youtube.com/@zitadel>

Copyright © 2025 All rights reserved.