├── api-jwt ├── screenshots │ ├── .gitkeep │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── 9.png │ ├── 10.png │ └── 11.png ├── api-key-file.json ├── .env ├── server.py ├── README.md └── validator.py ├── service-user-pat ├── screenshots │ ├── .gitkeep │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png └── README.md ├── service-user-jwt ├── screenshots │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── 9.png │ └── scopes │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ └── 9.png ├── client-key-file.json ├── .env ├── jwt-profile-token-generator.py └── README.md ├── api-basic-authentication ├── screenshots │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png ├── .env ├── server.py ├── README.md └── validator.py ├── service-user-client-credentials ├── screenshots │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png ├── .env ├── client-credentials-token-generator.py └── README.md ├── requirements.txt └── README.md /api-jwt/screenshots/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /service-user-pat/screenshots/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /api-jwt/screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-jwt/screenshots/1.png -------------------------------------------------------------------------------- /api-jwt/screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-jwt/screenshots/2.png -------------------------------------------------------------------------------- /api-jwt/screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-jwt/screenshots/3.png -------------------------------------------------------------------------------- /api-jwt/screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-jwt/screenshots/4.png -------------------------------------------------------------------------------- /api-jwt/screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-jwt/screenshots/5.png -------------------------------------------------------------------------------- /api-jwt/screenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-jwt/screenshots/6.png -------------------------------------------------------------------------------- /api-jwt/screenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-jwt/screenshots/7.png -------------------------------------------------------------------------------- /api-jwt/screenshots/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-jwt/screenshots/8.png -------------------------------------------------------------------------------- /api-jwt/screenshots/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-jwt/screenshots/9.png -------------------------------------------------------------------------------- /api-jwt/screenshots/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-jwt/screenshots/10.png -------------------------------------------------------------------------------- /api-jwt/screenshots/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-jwt/screenshots/11.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/1.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/2.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/3.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/4.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/5.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/6.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/7.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/8.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/9.png -------------------------------------------------------------------------------- /service-user-pat/screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-pat/screenshots/1.png -------------------------------------------------------------------------------- /service-user-pat/screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-pat/screenshots/2.png -------------------------------------------------------------------------------- /service-user-pat/screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-pat/screenshots/3.png -------------------------------------------------------------------------------- /service-user-pat/screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-pat/screenshots/4.png -------------------------------------------------------------------------------- /service-user-pat/screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-pat/screenshots/5.png -------------------------------------------------------------------------------- /service-user-pat/screenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-pat/screenshots/6.png -------------------------------------------------------------------------------- /service-user-pat/screenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-pat/screenshots/7.png -------------------------------------------------------------------------------- /api-basic-authentication/screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-basic-authentication/screenshots/1.png -------------------------------------------------------------------------------- /api-basic-authentication/screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-basic-authentication/screenshots/2.png -------------------------------------------------------------------------------- /api-basic-authentication/screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-basic-authentication/screenshots/3.png -------------------------------------------------------------------------------- /api-basic-authentication/screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-basic-authentication/screenshots/4.png -------------------------------------------------------------------------------- /api-basic-authentication/screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-basic-authentication/screenshots/5.png -------------------------------------------------------------------------------- /api-basic-authentication/screenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-basic-authentication/screenshots/6.png -------------------------------------------------------------------------------- /api-basic-authentication/screenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/api-basic-authentication/screenshots/7.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/scopes/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/scopes/1.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/scopes/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/scopes/2.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/scopes/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/scopes/3.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/scopes/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/scopes/4.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/scopes/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/scopes/5.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/scopes/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/scopes/6.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/scopes/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/scopes/7.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/scopes/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/scopes/8.png -------------------------------------------------------------------------------- /service-user-jwt/screenshots/scopes/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-jwt/screenshots/scopes/9.png -------------------------------------------------------------------------------- /service-user-client-credentials/screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-client-credentials/screenshots/1.png -------------------------------------------------------------------------------- /service-user-client-credentials/screenshots/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-client-credentials/screenshots/10.png -------------------------------------------------------------------------------- /service-user-client-credentials/screenshots/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-client-credentials/screenshots/11.png -------------------------------------------------------------------------------- /service-user-client-credentials/screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-client-credentials/screenshots/2.png -------------------------------------------------------------------------------- /service-user-client-credentials/screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-client-credentials/screenshots/3.png -------------------------------------------------------------------------------- /service-user-client-credentials/screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-client-credentials/screenshots/4.png -------------------------------------------------------------------------------- /service-user-client-credentials/screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-client-credentials/screenshots/5.png -------------------------------------------------------------------------------- /service-user-client-credentials/screenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-client-credentials/screenshots/6.png -------------------------------------------------------------------------------- /service-user-client-credentials/screenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-client-credentials/screenshots/7.png -------------------------------------------------------------------------------- /service-user-client-credentials/screenshots/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-client-credentials/screenshots/8.png -------------------------------------------------------------------------------- /service-user-client-credentials/screenshots/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zitadel/examples-api-access-and-token-introspection/HEAD/service-user-client-credentials/screenshots/9.png -------------------------------------------------------------------------------- /service-user-jwt/client-key-file.json: -------------------------------------------------------------------------------- 1 | {"type":"serviceaccount","keyId":"","key":"-----BEGIN RSA PRIVATE KEY-----\n\n-----END RSA PRIVATE KEY-----\n","userId":""} 2 | -------------------------------------------------------------------------------- /api-jwt/api-key-file.json: -------------------------------------------------------------------------------- 1 | {"type":"application","keyId":"","key":"-----BEGIN RSA PRIVATE KEY-----\n\n-----END RSA PRIVATE KEY-----\n","appId":"","clientId":""} 2 | -------------------------------------------------------------------------------- /api-jwt/.env: -------------------------------------------------------------------------------- 1 | API_PRIVATE_KEY_FILE="api-key-file.json" 2 | ZITADEL_DOMAIN="" 3 | ZITADEL_INTROSPECTION_URL="/oauth/v2/introspect" 4 | API_BASE_URL="http://localhost:5000" 5 | 6 | -------------------------------------------------------------------------------- /service-user-jwt/.env: -------------------------------------------------------------------------------- 1 | CLIENT_PRIVATE_KEY_FILE_PATH = "client-key-file.json" 2 | PROJECT_ID="" 3 | ZITADEL_DOMAIN = "" 4 | ZITADEL_TOKEN_URL = "/oauth/v2/token" 5 | 6 | -------------------------------------------------------------------------------- /api-basic-authentication/.env: -------------------------------------------------------------------------------- 1 | ZITADEL_DOMAIN="" 2 | ZITADEL_INTROSPECTION_URL="/oauth/v2/introspect" 3 | API_BASE_URL="http://localhost:5000" 4 | API_CLIENT_ID="" 5 | API_CLIENT_SECRET=" Response: 13 | response = jsonify(ex.error) 14 | response.status_code = ex.status_code 15 | return response 16 | 17 | @app.route("/api/public") 18 | def public(): 19 | response = "Public route - You don't need to be authenticated to see this." 20 | return jsonify(message=response) 21 | 22 | @app.route("/api/private") 23 | @require_auth(None) 24 | def private(): 25 | response = "Private route - You need to be authenticated to see this." 26 | return jsonify(message=response) 27 | 28 | @app.route("/api/private-scoped") 29 | @require_auth(["read:messages"]) 30 | def private_scoped(): 31 | response = "Private, scoped route - You need to be authenticated and have the role read:messages to see this." 32 | return jsonify(message=response) 33 | 34 | if __name__ == "__main__": 35 | app.run() 36 | -------------------------------------------------------------------------------- /api-basic-authentication/server.py: -------------------------------------------------------------------------------- 1 | # server.py 2 | from flask import Flask, jsonify, Response 3 | from authlib.integrations.flask_oauth2 import ResourceProtector 4 | from validator import ZitadelIntrospectTokenValidator, ValidatorError 5 | 6 | require_auth = ResourceProtector() 7 | require_auth.register_token_validator(ZitadelIntrospectTokenValidator()) 8 | 9 | app = Flask(__name__) 10 | 11 | @app.errorhandler(ValidatorError) 12 | def handle_auth_error(ex: ValidatorError) -> Response: 13 | response = jsonify(ex.error) 14 | response.status_code = ex.status_code 15 | return response 16 | 17 | @app.route("/api/public") 18 | def public(): 19 | response = "Public route - You don't need to be authenticated to see this." 20 | return jsonify(message=response) 21 | 22 | @app.route("/api/private") 23 | @require_auth(None) 24 | def private(): 25 | response = "Private route - You need to be authenticated to see this." 26 | return jsonify(message=response) 27 | 28 | @app.route("/api/private-scoped") 29 | @require_auth(["read:messages"]) 30 | def private_scoped(): 31 | response = "Private, scoped route - You need to be authenticated and have the role read:messages to see this." 32 | return jsonify(message=response) 33 | 34 | if __name__ == "__main__": 35 | app.run() 36 | -------------------------------------------------------------------------------- /service-user-client-credentials/client-credentials-token-generator.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | import base64 4 | from dotenv import load_dotenv 5 | 6 | load_dotenv() 7 | 8 | ZITADEL_DOMAIN = os.getenv("ZITADEL_DOMAIN") 9 | CLIENT_ID = os.getenv("CLIENT_ID") 10 | CLIENT_SECRET = os.getenv("CLIENT_SECRET") 11 | ZITADEL_TOKEN_URL = os.getenv("ZITADEL_TOKEN_URL") 12 | PROJECT_ID = os.getenv("PROJECT_ID") 13 | 14 | # Encode the client ID and client secret in Base64 15 | client_credentials = f"{CLIENT_ID}:{CLIENT_SECRET}".encode("utf-8") 16 | base64_client_credentials = base64.b64encode(client_credentials).decode("utf-8") 17 | 18 | # Request an OAuth token from ZITADEL 19 | headers = { 20 | "Content-Type": "application/x-www-form-urlencoded", 21 | "Authorization": f"Basic {base64_client_credentials}" 22 | } 23 | 24 | data = { 25 | "grant_type": "client_credentials", 26 | "scope": f"openid profile email urn:zitadel:iam:org:project:id:{PROJECT_ID}:aud read:messages" 27 | 28 | } 29 | 30 | response = requests.post(ZITADEL_TOKEN_URL, headers=headers, data=data) 31 | 32 | if response.status_code == 200: 33 | access_token = response.json()["access_token"] 34 | print(f"Response: {response.json()}") 35 | print(f"Access token: {access_token}") 36 | else: 37 | print(f"Error: {response.status_code} - {response.text}") 38 | -------------------------------------------------------------------------------- /service-user-jwt/jwt-profile-token-generator.py: -------------------------------------------------------------------------------- 1 | import json 2 | import time 3 | import requests 4 | import jwt 5 | import os 6 | from dotenv import load_dotenv 7 | 8 | load_dotenv() 9 | 10 | ZITADEL_DOMAIN = os.getenv("ZITADEL_DOMAIN") 11 | CLIENT_PRIVATE_KEY_FILE_PATH = os.getenv("CLIENT_PRIVATE_KEY_FILE_PATH") 12 | ZITADEL_TOKEN_URL = os.getenv("ZITADEL_TOKEN_URL") 13 | PROJECT_ID = os.getenv("PROJECT_ID") 14 | 15 | # Load the downloaded JSON file 16 | with open(CLIENT_PRIVATE_KEY_FILE_PATH, "r") as f: 17 | json_data = json.load(f) 18 | 19 | private_key = json_data["key"] 20 | kid = json_data["keyId"] 21 | user_id = json_data["userId"] 22 | 23 | # Create JWT header and payload 24 | header = { 25 | "alg": "RS256", 26 | "kid": kid 27 | } 28 | 29 | payload = { 30 | "iss": user_id, 31 | "sub": user_id, 32 | "aud": ZITADEL_DOMAIN, 33 | "iat": int(time.time()), 34 | "exp": int(time.time()) + 3600 35 | } 36 | 37 | # Sign the JWT 38 | jwt_token = jwt.encode(payload, private_key, algorithm='RS256', headers=header) 39 | 40 | # Request an OAuth token from ZITADEL 41 | data = { 42 | "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", 43 | "scope": f"openid profile email urn:zitadel:iam:org:project:id:{PROJECT_ID}:aud read:messages", 44 | "assertion": jwt_token 45 | } 46 | 47 | response = requests.post(ZITADEL_TOKEN_URL, data=data) 48 | 49 | if response.status_code == 200: 50 | access_token = response.json()["access_token"] 51 | print(f"Response: {response.json()}") 52 | print(f"Access token: {access_token}") 53 | else: 54 | print(f"Error: {response.status_code} - {response.text}") 55 | -------------------------------------------------------------------------------- /api-basic-authentication/README.md: -------------------------------------------------------------------------------- 1 | # Secure an API using Basic Authentication in ZITADEL 2 | 3 | ## ToC 4 | 1. [Register the API in ZITADEL](#1) 5 | 2. [Run this API](#2) 6 | 3. [Invoke this API](#3) 7 | 8 | 9 | ## 1. Register the API in ZITADEL 10 | 11 | 1. Go to your Project and click on the **New** button as shown below. 12 | 13 | Register the API 18 | 19 | 2. Give a name to your application (Test API 2 is the name given below) and select type **API**. 20 | 21 | Register the API 26 | 27 | 3. Select **Basic** as the authentication method and click **Continue**. 28 | 29 | Register the API 34 | 35 | 4. Now review your configuration and click **Create**. 36 | 37 | Register the API 42 | 43 | 5. You will now see the API’s **Client ID** and the **Client Secret**. Copy them and click **Close**. 44 | 45 | Register the API 50 | 51 | 6. When you click **URLs** on the left, you will see the relevant OIDC URLs. Note down the **issuer** URL, **token_endpoint** and **introspection_endpoint**. 52 | 53 | Register the API 58 | 59 | 7. Also note down the **Resource ID** of your project. 60 | 61 | Register the API 66 | 67 | ## 2. Run this API 68 | 69 | The API has three routes: 70 | 71 | - "/api/public" - No access token is required. 72 | - "/api/private" - A valid access token is required. 73 | - "/api/private-scoped" - A valid access token and a "read:messages" scope are required. 74 | 75 | 76 | 1. cd to this directory: `cd api-basic-authentication` 77 | 2. Replace the values of ZITADEL_DOMAIN, ZITADEL_INTROSPECTION_URL, API_CLIENT_ID and API_CLIENT_SECRET in the .env file with your values you obtained earlier. 78 | 3. Run the API by running `python3 server.py` in the terminal. 79 | 80 | 81 | ## 3. Invoke this API 82 | 83 | Obtain an access token as a ZITADEL service user via one of the following approaches (click the links to see instructions on how to obtain the token and invoke this API): 84 | - [JSON Web Token Profile](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/service-user-jwt) 85 | - [Client Credentials](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/service-user-client-credentials) 86 | - [Personal Access Token](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/service-user-pat) 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /api-basic-authentication/validator.py: -------------------------------------------------------------------------------- 1 | from os import environ as env 2 | import os 3 | import time 4 | from typing import Dict 5 | 6 | from authlib.oauth2.rfc7662 import IntrospectTokenValidator 7 | import requests 8 | from dotenv import load_dotenv, find_dotenv 9 | from requests.auth import HTTPBasicAuth 10 | 11 | load_dotenv() 12 | 13 | ZITADEL_DOMAIN = os.getenv("ZITADEL_DOMAIN") 14 | ZITADEL_INTROSPECTION_URL = os.getenv("ZITADEL_INTROSPECTION_URL") 15 | API_CLIENT_ID = os.getenv("API_CLIENT_ID") 16 | API_CLIENT_SECRET = os.getenv("API_CLIENT_SECRET") 17 | 18 | 19 | class ValidatorError(Exception): 20 | 21 | def __init__(self, error: Dict[str, str], status_code: int): 22 | super().__init__() 23 | self.error = error 24 | self.status_code = status_code 25 | 26 | class ZitadelIntrospectTokenValidator(IntrospectTokenValidator): 27 | def introspect_token(self, token_string): 28 | url = ZITADEL_INTROSPECTION_URL 29 | data = {'token': token_string, 'token_type_hint': 'access_token', 'scope': 'openid'} 30 | auth = HTTPBasicAuth(API_CLIENT_ID, API_CLIENT_SECRET) 31 | resp = requests.post(url, data=data, auth=auth) 32 | resp.raise_for_status() 33 | return resp.json() 34 | 35 | # def match_token_scopes(self, token, or_scopes): 36 | # if or_scopes is None: 37 | # return True 38 | # roles = token["urn:zitadel:iam:org:project:roles"].keys() 39 | # for and_scopes in or_scopes: 40 | # scopes = and_scopes.split() 41 | # """print(f"Check if all {scopes} are in {roles}")""" 42 | # if all(key in roles for key in scopes): 43 | # return True 44 | # return False 45 | 46 | def match_token_scopes(self, token, or_scopes): 47 | if or_scopes is None: 48 | return True 49 | scopes = token.get("scope", "").split() 50 | for and_scopes in or_scopes: 51 | if all(key in scopes for key in and_scopes.split()): 52 | return True 53 | return False 54 | 55 | def validate_token(self, token, scopes, request): 56 | print(f"Token: {token}\n") 57 | now = int(time.time()) 58 | if not token: 59 | raise ValidatorError({ 60 | "code": "invalid_token_revoked", 61 | "description": "Token was revoked." 62 | }, 401) 63 | if token["exp"] < now: 64 | raise ValidatorError({ 65 | "code": "invalid_token_expired", 66 | "description": "Token has expired." 67 | }, 401) 68 | if not token.get("active"): 69 | raise ValidatorError({ 70 | "code": "invalid_token_inactive", 71 | "description": "Token is inactive." 72 | }, 401) 73 | if not self.match_token_scopes(token, scopes): 74 | raise ValidatorError({ 75 | "code": "insufficient_scope", 76 | "description": f"Token has insufficient scope. Route requires: {scopes}" 77 | }, 401) 78 | 79 | 80 | def __call__(self, *args, **kwargs): 81 | res = self.introspect_token(*args, **kwargs) 82 | return res -------------------------------------------------------------------------------- /api-jwt/README.md: -------------------------------------------------------------------------------- 1 | # Secure an API using the JSON Web Token Profile in ZITADEL 2 | 3 | ## ToC 4 | 1. [Register the API in ZITADEL and Generate Private and Public Keys](#1) 5 | 2. [Run this API](#2) 6 | 3. [Invoke this API](#3) 7 | 8 | 9 | ## 1. Register the API in ZITADEL and Generate Private and Public Keys 10 | 11 | 1. Go to your Project and click on the **New** button as shown below. 12 | 13 | Register the API 18 | 19 | 2. Give a name to your application (Test API is the name given below) and select type **API**. 20 | 21 | Register the API 26 | 27 | 3. Select **JWT** as the authentication method and click **Continue**. 28 | 29 | Register the API 34 | 35 | 4. Now review your configuration and click **Create**. 36 | 37 | Register the API 42 | 43 | 5. You will now see the API’s **Client ID**. You will not see a Client Secret because we are using a private JWT key. 44 | 45 | Register the API 50 | 51 | 6. Next, we must create the key pairs. Click on **New**. 52 | 53 | Register the API 58 | 59 | 7. Select **JSON** as the type of key. You can also set an expiration time for the key or leave it empty. Click on **Add**. 60 | 61 | Register the API 66 | 67 | 8. Download the created key by clicking the **Download** button and then click **Close**. 68 | 69 | Register the API 74 | 75 | 9. The key will be downloaded. 76 | 77 | Register the API 82 | 83 | 10. When you click on URLs on the left, you will see the relevant OIDC URLs. Note down the **issuer** URL, **token_endpoint** and **introspection_endpoint**. 84 | 85 | Register the API 90 | 91 | 11. The key that you downloaded will be of the following format. 92 | ``` 93 | { 94 | "type":"application", 95 | "keyId":"", 96 | "key":"-----BEGIN RSA PRIVATE KEY-----\\n-----END RSA PRIVATE KEY-----\n", 97 | "appId":"", 98 | "clientId":"" 99 | } 100 | ``` 101 | 12. Also note down the **Resource ID** of your project. 102 | 103 | Register the API 108 | 109 | ## 2. Run this API 110 | 111 | The API has three routes: 112 | 113 | - "/api/public" - No access token is required. 114 | - "/api/private" - A valid access token is required. 115 | - "/api/private-scoped" - A valid access token and a "read:messages" scope are required. 116 | 117 | 118 | 1. cd to this directory: `cd api-jwt` 119 | 2. Copy the content in your downloaded key file to `api-key-file.json`. 120 | 3. Replace the values of ZITADEL_DOMAIN and ZITADEL_INTROSPECTION_URL in the .env file with your values you obtained earlier. 121 | 4. Run the API by running `python3 server.py` in the terminal. 122 | 123 | 124 | ## 3. Invoke this API 125 | 126 | Obtain an access token as a ZITADEL service user via one of the following approaches (click the links to see instructions on how to obtain the token and invoke this API): 127 | - [JSON Web Token Profile](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/service-user-jwt) 128 | - [Client Credentials](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/service-user-client-credentials) 129 | - [Personal Access Token](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/service-user-pat) 130 | 131 | 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # api-access-and-token-introspection 2 | 3 | Find a detailed explanation of the concepts covered in these examples in this [blog post](https://zitadel.com/blog/api-access-and-introspection). 4 | 5 | ## API Application: 6 | If you have an API that behaves as an OAuth resource server that can be accessed by user-facing applications and need to validate an access token by calling the ZITADEL introspection API, you can use the following methods to register these APIs in ZITADEL: 7 | 8 | - [JSON Web Token (JWT) Profile (Recommended)](https://zitadel.com/docs/apis/openidoauth/authn-methods#jwt-with-private-key) 9 | - [Test JWT Profile for API Applications](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-jwt) 10 | - [Basic Authentication](https://zitadel.com/docs/apis/openidoauth/authn-methods#client-secret-basic) 11 | - [Test Basic Authentication for API Applications](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-basic-authentication) 12 | 13 | 14 | 15 | ## Service Users: 16 | If there are client APIs or systems that need to access other protected APIs, these APIs or systems must be declared as service users. A service user is not considered an application type in ZITADEL. The following mechanisms are available for service users to obtain an access token: 17 | 18 | - [JSON Web Token (JWT) Profile (Recommended)](https://zitadel.com/docs/guides/integrate/serviceusers) 19 | - [Test JWT Profile for Service Users](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/service-user-jwt) 20 | - [Client Credentials](https://zitadel.com/docs/guides/integrate/client-credential) 21 | - [Test Client Credentials for Serivce Users](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/service-user-client-credentials) 22 | - [Personal Access Tokens (PAT)](https://zitadel.com/docs/guides/integrate/pat) 23 | - [Test Personal Access Tokens for Service Users](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/service-user-pat) 24 | 25 | 26 | 27 | ## All Possible Combinations: 28 | | **#** | **Grant and Token Type Used by Service User** | **How the Application API Invokes the Introspection Endpoint** | 29 | |-------|---------------------------------------------------------------------------------------|----------------------------------------------------------------| 30 | | 1 | Send JWT to ZITADEL to receive an opaque token or JWT and send the received token to API (JWT Profile) | JWT Profile | 31 | | 2 | Send JWT to ZITADEL to receive an opaque token or JWT and send the received token to API (JWT Profile) | Basic Authentication | 32 | | 3 | Send Client ID and Client Secret to ZITADEL and receive an opaque token or JWT and send the received token to API (Client Credentials) | JWT Profile | 33 | | 4 | Send Client ID and Client Secret to ZITADEL and receive an opaque token or JWT and send the received token to API (Client Credentials) | Basic Authentication | 34 | | 5 | Send a static access token to API (Personal Access Token) | JWT Profile | 35 | | 6 | Send a static access token to API (Personal Access Token) | Basic Authentication | 36 | 37 | 38 | ## Prerequisites to Run the Samples: 39 | 40 | - Clone this repository. 41 | - Have python3 and pip3 installed in your machine. 42 | - Install required dependencies by running `pip3 install -r requirements.txt` on your terminal. 43 | - Create a free ZITADEL account here - https://zitadel.cloud/ 44 | - Create an instance as explained [here](https://zitadel.com/docs/guides/start/quickstart#2-create-your-first-instance). 45 | - Create a new project in your instance by following the steps [here](https://zitadel.com/docs/guides/start/quickstart#2-create-your-first-instance). 46 | - Make sure that you replace the values in the .env file in each project with the values you obtain from ZITADEL. 47 | 48 | 49 | -------------------------------------------------------------------------------- /api-jwt/validator.py: -------------------------------------------------------------------------------- 1 | from os import environ as env 2 | import os 3 | import time 4 | import json 5 | import jwt 6 | from typing import Dict 7 | 8 | from authlib.oauth2.rfc7662 import IntrospectTokenValidator 9 | import requests 10 | from dotenv import load_dotenv, find_dotenv 11 | from requests.auth import HTTPBasicAuth 12 | 13 | 14 | load_dotenv(find_dotenv()) 15 | 16 | 17 | ZITADEL_DOMAIN = os.getenv("ZITADEL_DOMAIN") 18 | ZITADEL_INTROSPECTION_URL = os.getenv("ZITADEL_INTROSPECTION_URL") 19 | API_PRIVATE_KEY_FILE_PATH = os.getenv("API_PRIVATE_KEY_FILE") 20 | API_PRIVATE_KEY_FILE = {} 21 | 22 | class ValidatorError(Exception): 23 | def __init__(self, error: Dict[str, str], status_code: int): 24 | super().__init__() 25 | self.error = error 26 | self.status_code = status_code 27 | 28 | 29 | class ZitadelIntrospectTokenValidator(IntrospectTokenValidator): 30 | def __init__(self, *args, **kwargs): 31 | super().__init__(*args, **kwargs) 32 | self.load_api_private_key(API_PRIVATE_KEY_FILE_PATH) 33 | 34 | @staticmethod 35 | def load_api_private_key(file_path): 36 | with open(file_path, 'r') as f: 37 | data = json.load(f) 38 | API_PRIVATE_KEY_FILE['client_id'] = data['clientId'] 39 | API_PRIVATE_KEY_FILE['key_id'] = data['keyId'] 40 | API_PRIVATE_KEY_FILE['private_key'] = data['key'] 41 | 42 | def introspect_token(self, token_string): 43 | # Create JWT for client assertion 44 | payload = { 45 | "iss": API_PRIVATE_KEY_FILE["client_id"], 46 | "sub": API_PRIVATE_KEY_FILE["client_id"], 47 | "aud": ZITADEL_DOMAIN, 48 | "exp": int(time.time()) + 60 * 60, # Expires in 1 hour 49 | "iat": int(time.time()) 50 | } 51 | headers = { 52 | "alg": "RS256", 53 | "kid": API_PRIVATE_KEY_FILE["key_id"] 54 | } 55 | jwt_token = jwt.encode(payload, API_PRIVATE_KEY_FILE["private_key"], algorithm="RS256", headers=headers) 56 | 57 | # Send introspection request 58 | headers = {"Content-Type": "application/x-www-form-urlencoded"} 59 | data = { 60 | "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", 61 | "client_assertion": jwt_token, 62 | "token": token_string 63 | } 64 | response = requests.post(ZITADEL_INTROSPECTION_URL, headers=headers, data=data) 65 | response.raise_for_status() 66 | token_data = response.json() 67 | print(f"Token data from introspection: {token_data}") 68 | return token_data 69 | 70 | 71 | def match_token_scopes(self, token, or_scopes): 72 | if or_scopes is None: 73 | return True 74 | scopes = token.get("scope", "").split() 75 | for and_scopes in or_scopes: 76 | if all(key in scopes for key in and_scopes.split()): 77 | return True 78 | return False 79 | 80 | def validate_token(self, token, scopes, request): 81 | print(f"Token: {token}\n") 82 | now = int(time.time()) 83 | if not token: 84 | raise ValidatorError({ 85 | "code": "invalid_token_revoked", 86 | "description": "Token was revoked." 87 | }, 401) 88 | if token["exp"] < now: 89 | raise ValidatorError({ 90 | "code": "invalid_token_expired", 91 | "description": "Token has expired." 92 | }, 401) 93 | if not token.get("active"): 94 | raise ValidatorError({ 95 | "code": "invalid_token_inactive", 96 | "description": "Token is inactive." 97 | }, 401) 98 | if not self.match_token_scopes(token, scopes): 99 | raise ValidatorError({ 100 | "code": "insufficient_scope", 101 | "description": f"Token has insufficient scope. Route requires: {scopes}" 102 | }, 401) 103 | 104 | def __call__(self, token_string, scopes, request): 105 | token = self.introspect_token(token_string) 106 | self.validate_token(token, scopes, request) 107 | return token 108 | -------------------------------------------------------------------------------- /service-user-pat/README.md: -------------------------------------------------------------------------------- 1 | # Call a Secured API Using a Personal Access Token (PAT) 2 | 3 | ## ToC 4 | 1. [Prerequisites](#1) 5 | 2. [Create a Service User and Personal Access Token(PAT) in ZITADEL](#2) 6 | 3. [Invoke the API with Token](#3) 7 | 8 | ## 1. Prerequisites 9 | 10 | You must have the backend API running and secured with one of the following ways (follow either link for instructions on how to set up and run the API): 11 | 12 | 1. [JSON Web Token (JWT) Profile](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-jwt) 13 | 2. [Basic Authentication](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-basic-authentication) 14 | 15 | ## 2. Create a Service User with a Personal Access Token(PAT) in ZITADEL 16 | 17 | 1. Go to the **Users** tab in your organization as shown below and click on the **Service Users** tab. To add a service user, click on the **New** button. 18 | 19 | Register the API 24 | 25 | 26 | 2. Next, add the details of the service user and select either **Bearer** or **JWT** for **Access Token Type** and click on **Create**. For this example, we will select **Bearer**. 27 | 28 | Register the API 33 | 34 | 35 | 3. Click on **Personal Access Tokens**. 36 | 37 | Register the API 42 | 43 | 44 | 4. You can create one or more PATs for your user. We will create one now. Click on **New**. 45 | 46 | Register the API 51 | 52 | 53 | 5. Select an expiration date for the token or you can leave it empty. Click **Add**. 54 | 55 | Register the API 60 | 61 | 62 | 6. Copy the generated access token and click **Close**. 63 | 64 | Register the API 69 | 70 | 71 | 7. You will see the token listed for the service user. 72 | 73 | Register the API 78 | 79 | 80 | 81 | ## 3. Invoke the API with Token 82 | 83 | The API has three routes: 84 | 85 | - "/api/public" - No access token is required. 86 | - "/api/private" - A valid access token is required. 87 | - "/api/private-scoped" - A valid access token and a "read:messages" scope are required. 88 | 89 | 1. Invoke the public resource using the following cURL command: 90 | 91 | `curl -X GET http://localhost:5000/api/public` 92 | 93 | You should get a response with Status Code 200 and the following message. 94 | 95 | `{"message":"Public route - You don't need to be authenticated to see this."}` 96 | 97 | 2. Invoke the private resource: 98 | 99 | Copy the Personal Access Token and assign it to a shell variable called TOKEN as shown below: 100 | 101 | `TOKEN=` 102 | 103 | Invoke the private resource using the following cURL command 104 | 105 | `curl -X GET -H "Authorization: Bearer $TOKEN" http://localhost:5000/api/private` 106 | 107 | You should get a response with Status Code 200 and the following message. 108 | 109 | `{"message":"Private route - You need to be authenticated to see this."}` 110 | 111 | If you invoke the same resource without an access token (i.e., `curl -X GET http://localhost:5000/api/private`), you will see a 401 error. 112 | 113 | 3. Invoke the private route that requires the user to have a certain role using the following cURL command: 114 | 115 | `curl -X GET -H "Authorization: Bearer $TOKEN" http://localhost:5000/api/private-scoped` 116 | 117 | You should then get a Status Code 403, Forbidden error because the user does not have the role `read:messages`. 118 | 119 | In order to access this route, you must create the role `read:messages` in your ZITADEL project and also create an authorization for the service user you created by adding the role to the user. Follow these [instructions](https://github.com/dakshitha/api-access-and-token-introspection/blob/main/service-user-jwt/README.md#41-create-a-role-and-assign-the-role-to-the-service-user-) to do so. 120 | 121 | Once you have assigned the role to the user, generate another PAT and invoke the protected resource with that PAT (remember to reset the TOKEN variable to the new PAT in the command line): 122 | 123 | `curl -X GET -H "Authorization: Bearer $TOKEN" http://localhost:5000/api/private-scoped` 124 | 125 | You should get a response with Status Code 200 and the following message. 126 | 127 | `{"message":"Private, scoped route - You need to be authenticated and have the role read:messages to see this."}` 128 | 129 | 130 | -------------------------------------------------------------------------------- /service-user-client-credentials/README.md: -------------------------------------------------------------------------------- 1 | # Call a Secured API Using Client Credentials 2 | 3 | ## ToC 4 | 1. [Prerequisites](#1) 5 | 2. [Create a Service User and Private/Public Keys in ZITADEL](#2) 6 | 3. [Generate a Token](#3) 7 | 4. [Invoke the API with Token](#4) 8 | 9 | ## 1. Prerequisites 10 | 11 | You must have the backend API running and secured with one of the following ways (follow either link for instructions on how to set up and run the API): 12 | 13 | 1. [JSON Web Token (JWT) Profile](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-jwt) 14 | 2. [Basic Authentication](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-basic-authentication) 15 | 16 | ## 2. Create a Service User with Client Credentials in ZITADEL 17 | 18 | 1. Go to the **Users** tab in your organization as shown below and click on the **Service Users** tab. To add a service user, click on the **New** button. 19 | 20 | Register the API 25 | 26 | 27 | 2. Next, add the details of the service user and select either **Bearer** or **JWT** for **Access Token Type** and click on **Create**. For this example, we will select **Bearer**. 28 | 29 | Register the API 34 | 35 | 36 | 3. Click on **Actions**. 37 | 38 | Register the API 43 | 44 | 45 | 4. Select **Generate Client Secret**. 46 | 47 | Register the API 52 | 53 | 54 | 5. Copy your Client ID and Client Secret. Click **Close**. 55 | 56 | Register the API 61 | 62 | 63 | 6. Next we will add a role to the created service user. Click on **Authorizations** and click on **New** as shown below: 64 | 65 | Register the API 70 | 71 | 72 | 7. The user is already selected for you. Now select the **Project**. 73 | 74 | Register the API 79 | 80 | 81 | 8. You will see the following screen afterwards. Click on **Continue**. 82 | 83 | Register the API 88 | 89 | 9. Select the role **read:messages** and click **Save**. 90 | 91 | Register the API 96 | 97 | 10. You will now see that your service user has been assigned the role **read:messages**. 98 | 99 | Register the API 104 | 105 | 106 | ## 3. Generate a Token 107 | 108 | 1. cd to this directory: `cd service-user-client-credentials` 109 | 2. Replace the values of PROJECT_ID, ZITADEL_DOMAIN, ZITADEL_TOKEN_URL, CLIENT_ID, and CLIENT_SECRET in the .env file with your values you obtained earlier. 110 | 3. Run the script to generate a token by running `python3 client-credentials-token-generator.py` in the terminal. 111 | 4. Copy the printed access token and set the value to a shell variable called `TOKEN` as shown below: 112 | 113 | Register the API 118 | 119 | ## 4. Invoke the API with Token 120 | 121 | The API has three routes: 122 | 123 | - "/api/public" - No access token is required. 124 | - "/api/private" - A valid access token is required. 125 | - "/api/private-scoped" - A valid access token and a "read:messages" scope are required. 126 | 127 | 1. Invoke the public resource using the following cURL command: 128 | 129 | `curl -X GET http://localhost:5000/api/public` 130 | 131 | You should get a response with Status Code 200 and the following message. 132 | 133 | `{"message":"Public route - You don't need to be authenticated to see this."}` 134 | 135 | 2. Invoke the private resource using the following cURL command: 136 | 137 | `curl -X GET -H "Authorization: Bearer $TOKEN" http://localhost:5000/api/private` 138 | 139 | You should get a response with Status Code 200 and the following message. 140 | 141 | `{"message":"Private route - You need to be authenticated to see this."}` 142 | 143 | If you invoke the same resource without an access token (i.e., `curl -X GET http://localhost:5000/api/private`), you will see a 401 error. 144 | 145 | 3. Invoke the private route that requires the user to have a certain role using the following cURL command: 146 | 147 | `curl -X GET -H "Authorization: Bearer $TOKEN" http://localhost:5000/api/private-scoped` 148 | 149 | You should get a response with Status Code 200 and the following message. 150 | 151 | `{"message":"Private, scoped route - You need to be authenticated and have the role read:messages to see this."}` 152 | 153 | If you remove the role `read:messages` from your service user in the ZITADEL console and invoke this resource again, you should then get a Status Code 403, Forbidden error because the user does not have the role `read:messages`. 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /service-user-jwt/README.md: -------------------------------------------------------------------------------- 1 | # Call a Secured API Using JSON Web Token (JWT) Profile 2 | 3 | ## ToC 4 | 1. [Prerequisites](#1) 5 | 2. [Create a Service User and Private/Public Keys in ZITADEL](#2) 6 | 3. [Generate a Token](#3) 7 | 4. [Invoke the API](#4) 8 | 9 | ## 1. Prerequisites 10 | 11 | You must have the backend API running and secured with one of the following ways (follow either link for instructions on how to set up and run the API): 12 | 13 | 1. [JSON Web Token (JWT) Profile](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-jwt) 14 | 2. [Basic Authentication](https://github.com/zitadel/examples-api-access-and-token-introspection/tree/main/api-basic-authentication) 15 | 16 | ## 2. Create a Service User and Private/Public Keys in ZITADEL 17 | 18 | 1. Go to the **Users** tab in your organization as shown below and click on the **Service Users** tab. 19 | 20 | Register the API 25 | 26 | 27 | 2. To add a service user, click on the **New** button. 28 | 29 | Register the API 34 | 35 | 36 | 3. Next, add the details of the service user and select either **Bearer** or **JWT** for **Access Token Type** and click on **Create**. For this example, we will select **JWT**. 37 | 38 | Register the API 43 | 44 | 45 | 4. Now you will see the saved details. 46 | 47 | Register the API 52 | 53 | 54 | 5. Next, we need to generate a private-public key pair in ZITADEL and you must get the private key to sign your JWT. Go to **Keys** and click on **New**. 55 | 56 | Register the API 61 | 62 | 63 | 6. Select type **JSON** and click **Add**. 64 | 65 | Register the API 70 | 71 | 72 | 7. Download the key by clicking **Download**. After the download, click **Close**. 73 | 74 | Register the API 79 | 80 | 81 | 8. You will see the following screen afterwards. 82 | 83 | Register the API 88 | 89 | 90 | 9. The downloaded key will be of the following format: 91 | 92 | ``` 93 | { 94 | "type":"serviceaccount", 95 | "keyId":"", 96 | "key":"-----BEGIN RSA PRIVATE KEY-----\n\n-----END RSA PRIVATE KEY-----\n", 97 | "userId":"" 98 | } 99 | ``` 100 | 101 | ## 3. Generate a Token 102 | 103 | 1. cd to this directory: `cd service-user-jwt` 104 | 2. Copy the content in your downloaded key file to `client-key-file.json`. 105 | 3. Replace the values of PROJECT_ID, ZITADEL_DOMAIN and ZITADEL_TOKEN_URL in the `.env file` with your values you obtained earlier. 106 | 4. Run the script to generate a token by running `python3 jwt-profile-token-generator.py` in the terminal. 107 | 5. Copy the printed access token and set the value to a shell variable called `TOKEN` as shown below: 108 | 109 | Register the API 114 | 115 | ## 4. Invoke the API with Token 116 | 117 | The API has three routes: 118 | 119 | - "/api/public" - No access token is required. 120 | - "/api/private" - A valid access token is required. 121 | - "/api/private-scoped" - A valid access token and a "read:messages" scope are required. 122 | 123 | 1. Invoke the public resource using the following cURL command: 124 | 125 | `curl -X GET http://localhost:5000/api/public` 126 | 127 | You should get a response with Status Code 200 and the following message. 128 | 129 | `{"message":"Public route - You don't need to be authenticated to see this."}` 130 | 131 | 2. Invoke the private resource using the following cURL command: 132 | 133 | `curl -X GET -H "Authorization: Bearer $TOKEN" http://localhost:5000/api/private` 134 | 135 | You should get a response with Status Code 200 and the following message. 136 | 137 | `{"message":"Private route - You need to be authenticated to see this."}` 138 | 139 | If you invoke the same resource without an access token (i.e., `curl -X GET http://localhost:5000/api/private`), you will see a 401 error. 140 | 141 | 3. Invoke the private route that requires the user to have a certain role using the following cURL command: 142 | 143 | `curl -X GET -H "Authorization: Bearer $TOKEN" http://localhost:5000/api/private-scoped` 144 | 145 | You should then get a Status Code 403, Forbidden error because the user does not have the role `read:messages`. 146 | 147 | ### 4.1 Create a Role and Assign the Role to the Service User 148 | 149 | In order to access this route, you must create the role `read:messages` in your ZITADEL project and also create an authorization for the service user you created by adding the role to the user. Follow these steps to do so: 150 | 151 | 1. Go to your project and select **Roles**. Click **New**. 152 | 153 | Register the API 158 | 159 | 2. Add the `read:messages` role as shown below and click **Save**. 160 | 161 | Register the API 166 | 167 | 3. You will see the created role listed. 168 | 169 | Register the API 174 | 175 | 4. To assign this role to a user, click on **Authorizations**. 176 | 177 | Register the API 182 | 183 | 5. Select the user you want to assign the role to. 184 | 185 | Register the API 190 | 191 | 6. Select the project where this authorization is applicable. 192 | 193 | Register the API 198 | 199 | 7. Click **Continue**. 200 | 201 | Register the API 206 | 207 | 8. Select the role **read:messages** and click **Save**. 208 | 209 | Register the API 214 | 215 | 9. You will now see the your service user has been assigned the role **read:messages**. 216 | 217 | Register the API 222 | 223 | Regenerate the token (remember to assign the TOKEN to the new access token) and invoke the protected resource again: 224 | 225 | `curl -X GET -H "Authorization: Bearer $TOKEN" http://localhost:5000/api/private-scoped` 226 | 227 | You should get a response with Status Code 200 and the following message. 228 | 229 | `{"message":"Private, scoped route - You need to be authenticated and have the role read:messages to see this."}` 230 | 231 | ## 5. Reference 232 | https://zitadel.com/docs/guides/integrate/serviceusers#3-with-this-jwt-request-an-oauth-token-from-zitadel 233 | 234 | --------------------------------------------------------------------------------