mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
<!-- Please inform yourself about the contribution guidelines on submitting a PR here: https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr. Take note of how PR/commit titles should be written and replace the template texts in the sections below. Don't remove any of the sections. It is important that the commit history clearly shows what is changed and why. Important: By submitting a contribution you agree to the terms from our Licensing Policy as described here: https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions. --> # Which Problems Are Solved As part of the consistent naming effort, this PR focuses on Resource ID's. # How the Problems Are Solved Renamed generic Resource ID to matching Object + ID such as - Organization ID - User ID - Application ID - Identity Provider ID - Instance ID - Project ID # Additional Changes None # Additional Context - Closes #11306 --------- Co-authored-by: Livio Spring <[email protected]>
201 lines
7.2 KiB
Plaintext
201 lines
7.2 KiB
Plaintext
---
|
||
title: Private Key JWT
|
||
sidebar_label: Private Key JWT
|
||
---
|
||
|
||
import IntrospectionResponse from './_introspection-response.mdx';
|
||
|
||
This guide explains how to use ZITADEL's [Private Key JWT authentication method](/apis/openidoauth/authn-methods#jwt-with-private-key) to authenticate your application and call the token introspection endpoint. This is not about securing an API, but about allowing your backend to introspect access tokens in a secure way.
|
||
|
||
If you're looking to set up Private Key JWT authentication specifically for Service Accounts, see our dedicated guide [here](/guides/integrate/service-accounts/private-key-jwt).
|
||
|
||
## Register an Application & Generate a Key Pair
|
||
|
||
To use Private Key JWT for introspection, you must register your client application in ZITADEL and create a key pair. This process enables your application to obtain credentials needed to generate JWT assertions for authentication.
|
||
|
||
1. In your ZITADEL project, click **New** to add an application.
|
||
|
||
<img
|
||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-1.png"
|
||
width="75%"
|
||
alt="Register the app"
|
||
/>
|
||
|
||
2. Enter a name (e.g., "Backend Client") and select **API** as the application type.
|
||
|
||
<img
|
||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-2.png"
|
||
width="75%"
|
||
alt="Set application type"
|
||
/>
|
||
|
||
3. Choose **JWT** as the authentication method and click **Continue**.
|
||
|
||
<img
|
||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-3.png"
|
||
width="75%"
|
||
alt="Choose JWT Profile"
|
||
/>
|
||
|
||
4. Review your settings and click **Create**.
|
||
|
||
<img
|
||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-4.png"
|
||
width="75%"
|
||
alt="Create application"
|
||
/>
|
||
|
||
5. After creation, you’ll see the application’s **Client ID**. There is no client secret—instead, authentication relies on your private key and JWT.
|
||
|
||
<img
|
||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-5.png"
|
||
width="75%"
|
||
alt="Client ID"
|
||
/>
|
||
|
||
6. To generate a key pair, click **New** under the application’s keys section.
|
||
|
||
<img
|
||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-6.png"
|
||
width="75%"
|
||
alt="Generate a new key"
|
||
/>
|
||
|
||
7. Select **JSON** as the key type, set an expiration if desired, and click **Add**.
|
||
|
||
<img
|
||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-7.png"
|
||
width="75%"
|
||
alt="Key details"
|
||
/>
|
||
|
||
8. Download and save the generated key by clicking **Download**. Afterward, click **Close**.
|
||
|
||
<img
|
||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-8.png"
|
||
width="75%"
|
||
alt="Download key"
|
||
/>
|
||
|
||
9. The downloaded key file will look like this:
|
||
|
||
```
|
||
{
|
||
"type": "application",
|
||
"keyId": "<YOUR_KEY_ID>",
|
||
"key": "-----BEGIN RSA PRIVATE KEY-----\n<YOUR_PRIVATE_KEY>\n-----END RSA PRIVATE KEY-----\n",
|
||
"appId": "<YOUR_APP_ID>",
|
||
"clientId": "<YOUR_CLIENT_ID>"
|
||
}
|
||
```
|
||
|
||
10. In the left menu, select **URLs** to view the application’s OIDC endpoints. Note the **issuer** URL, **token_endpoint**, and **introspection_endpoint**.
|
||
|
||
<img
|
||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-10.png"
|
||
width="75%"
|
||
alt="OIDC Endpoints"
|
||
/>
|
||
|
||
11. Optionally, note your **Project ID** for reference.
|
||
|
||
<img
|
||
src="/docs/img/guides/integrate/token-introspection-jwt-profile-11.png"
|
||
width="75%"
|
||
alt="Project ID"
|
||
/>
|
||
|
||
## Calling the Introspection Endpoint
|
||
|
||
To introspect an access token in ZITADEL, your backend must authenticate by providing a JWT (`client_assertion`) signed with your application's private key. ZITADEL verifies this JWT to ensure the request is from a trusted application.
|
||
|
||
**Required request parameters:**
|
||
|
||
| **Parameter** | **Description** |
|
||
|------------------------- |---------------------------------------------------------------------------------------------------------------------|
|
||
| `client_assertion` | The JWT assertion created and signed as described below. |
|
||
| `client_assertion_type` | Must be `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
|
||
| `token` | The access token you want to introspect. |
|
||
|
||
### Creating the Client Assertion (JWT)
|
||
|
||
The `client_assertion` parameter is a signed JWT with the following structure:
|
||
|
||
**Header:**
|
||
```json
|
||
{
|
||
"alg": "RS256",
|
||
"kid": "<YOUR_KEY_ID>"
|
||
}
|
||
```
|
||
|
||
**Payload:**
|
||
```json
|
||
{
|
||
"iss": "<YOUR_CLIENT_ID>",
|
||
"sub": "<YOUR_CLIENT_ID>",
|
||
"aud": "https://${YOUR_DOMAIN}", // Your ZITADEL issuer URL
|
||
"exp": 1605183582, // Expiry (Unix timestamp)
|
||
"iat": 1605179982 // Issued at (Unix timestamp, not older than 1 hour)
|
||
}
|
||
```
|
||
|
||
- `iss` and `sub`: Your application’s Client ID.
|
||
- `aud`: The issuer URL for your ZITADEL instance (e.g. `https://my-tenant.zitadel.cloud`).
|
||
- `exp`: JWT expiration time (seconds since epoch).
|
||
- `iat`: JWT issued at time (seconds since epoch), must not be older than 1 hour.
|
||
|
||
You can generate the signed JWT using libraries in your preferred language or helper tools like [zitadel-tools](https://github.com/zitadel/zitadel-tools) or [https://dinochiesa.github.io/jwt/](https://dinochiesa.github.io/jwt/).
|
||
|
||
### Example Introspection HTTP Request
|
||
|
||
Make a POST request to the introspection endpoint, sending the above parameters:
|
||
|
||
```bash
|
||
curl --request POST \
|
||
--url ${YOUR_DOMAIN}/oauth/v2/introspect \
|
||
--header 'Content-Type: application/x-www-form-urlencoded' \
|
||
--data client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
|
||
--data client_assertion=eyJhbGciOiJSUzI1Ni... \
|
||
--data token=VjVxyCZmRmWYqd3_F5db9Pb9mHR
|
||
```
|
||
|
||
### Python Example
|
||
|
||
Below is a Python function that generates the JWT assertion and performs token introspection:
|
||
|
||
```python
|
||
def introspect_token(token_string):
|
||
# Build JWT for client assertion
|
||
payload = {
|
||
"iss": API_KEY_FILE["clientId"],
|
||
"sub": API_KEY_FILE["clientId"],
|
||
"aud": ZITADEL_ISSUER_URL,
|
||
"exp": int(time.time()) + 60 * 60, # expires in 1 hour
|
||
"iat": int(time.time())
|
||
}
|
||
headers = {
|
||
"alg": "RS256",
|
||
"kid": API_KEY_FILE["keyId"]
|
||
}
|
||
jwt_token = jwt.encode(payload, API_KEY_FILE["key"], algorithm="RS256", headers=headers)
|
||
|
||
req_headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
||
req_data = {
|
||
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
|
||
"client_assertion": jwt_token,
|
||
"token": token_string
|
||
}
|
||
response = requests.post(ZITADEL_INTROSPECTION_ENDPOINT, headers=req_headers, data=req_data)
|
||
response.raise_for_status()
|
||
token_data = response.json()
|
||
print(f"Token data from introspection: {token_data}")
|
||
return token_data
|
||
```
|
||
|
||
## Introspection Response
|
||
|
||
<IntrospectionResponse components={props.components} />
|
||
|
||
For more details and a complete working example, see this [tutorial](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-jwt) on registering an application in ZITADEL and calling the introspection endpoint with a JWT assertion.
|