Angel
Angel2d ago

Need help to introspect with JWT

Hi, I am trying to request introspect endpoint of my self hosted Zitadel like https://zitadel.com/docs/guides/integrate/token-introspection/private-key-jwt
def introspect_token(self, token_string):
url = f'{ZITADEL_DOMAIN}/oauth/v2/introspect'
payload = {
"iss": API_PRIVATE_KEY_FILE["clientId"],
"sub": API_PRIVATE_KEY_FILE["clientId"],
"aud": ZITADEL_DOMAIN,
"exp": int(time.time()) + 60 * 60,
"iat": int(time.time())
}
headers = {
"alg": "RS256",
"kid": API_PRIVATE_KEY_FILE["keyId"]
}

jwt_token = jwt.encode(payload, API_PRIVATE_KEY_FILE["key"], algorithm="RS256", headers=headers)

headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": jwt_token,
"token": token_string
}
response = requests.post(url, headers=headers, data=data)

response.raise_for_status()
token_data = response.json()
print(f"Token data from introspection: {token_data}")
return token_data
def introspect_token(self, token_string):
url = f'{ZITADEL_DOMAIN}/oauth/v2/introspect'
payload = {
"iss": API_PRIVATE_KEY_FILE["clientId"],
"sub": API_PRIVATE_KEY_FILE["clientId"],
"aud": ZITADEL_DOMAIN,
"exp": int(time.time()) + 60 * 60,
"iat": int(time.time())
}
headers = {
"alg": "RS256",
"kid": API_PRIVATE_KEY_FILE["keyId"]
}

jwt_token = jwt.encode(payload, API_PRIVATE_KEY_FILE["key"], algorithm="RS256", headers=headers)

headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": jwt_token,
"token": token_string
}
response = requests.post(url, headers=headers, data=data)

response.raise_for_status()
token_data = response.json()
print(f"Token data from introspection: {token_data}")
return token_data
But I got this response :
{
"code": "invalid_token",
"description": "Invalid token (active: false)"
}
{
"code": "invalid_token",
"description": "Invalid token (active: false)"
}
It works with a Basic Auth :
def introspect_token(self, token_string):
url = f'{ZITADEL_DOMAIN}/oauth/v2/introspect'
data = {'token': token_string, 'token_type_hint': 'access_token', 'scope': 'openid'}
auth = HTTPBasicAuth(CLIENT_ID2, CLIENT_SECRET)
resp = requests.post(url, data=data, auth=auth)
resp.raise_for_status()
return resp.json()
def introspect_token(self, token_string):
url = f'{ZITADEL_DOMAIN}/oauth/v2/introspect'
data = {'token': token_string, 'token_type_hint': 'access_token', 'scope': 'openid'}
auth = HTTPBasicAuth(CLIENT_ID2, CLIENT_SECRET)
resp = requests.post(url, data=data, auth=auth)
resp.raise_for_status()
return resp.json()
Furthermore I have a question, why client_id is not equal to sub (which is the ID of User inside Zitadel Console) and what is the ID that will never change because i wanna associate my app user with zitadel user Thank you for your help
ZITADEL Docs
This is a guide on how to secure your API using JSON Web Token (JWT) profile (recommended).
6 Replies
Ask Inkeep
Ask Inkeep2d ago
Hi @Angel, I'm an AI assistant for ZITADEL. While a team member reviews this question, I'll see if I can find relevant sources for your question. Answer When using JWT profile for introspection in ZITADEL, the introspection endpoint expects a client_assertion JWT signed with your application's private key and the token you want to introspect as the token parameter (1) (2) (3) . The values for iss and sub in the client_assertion JWT should be your application's client_id, not a user ID (1) (2) . The response "Invalid token (active: false) " typically means the token is not valid, expired, revoked, or not intended for your client/application . Make sure the token you provide was issued for your application, hasn't expired, and that you have the correct audience and scopes . Regarding identifiers: client_id refers to the application's registered client identifier. sub in tokens is the subject for which the token was issued—this can be a user or a client, depending on context . To uniquely associate your app user with a ZITADEL user, use the user's unique ID found in the ZITADEL Console (userId field) ; this is stable and will not change (4) . Reference: - JSON Web Token Profile in ZITADEL - OpenID Connect Endpoints in ZITADEL - Configure private key JWT authentication for service users - [What is the body 'token' param used for in token introspection?](https://github.com/zitadel/zitadel/discussion s/7814) ​ To learn more, see the sources I considered: [1] JSON Web Token Profile in ZITADEL [2] OpenID Connect Endpoints in ZITADEL [3] What is the body 'token' param used for in token introspection? [4] Configure private key JWT authentication for service users ​ Feel free to tag @Ask Inkeep with additional questions.
Angel
AngelOP2d ago
@Ask Inkeep with introspect endpoint sub is always the stable id ?
Ask Inkeep
Ask Inkeep2d ago
Thanks for the follow-up, @Angel. I'll get back to you soon. Answer The documentation does not explicitly state that the sub (subject) in the introspection endpoint response is always the stable user ID in ZITADEL. The sub claim is typically used as the unique identifier for the subject of the token, but the sources do not confirm if this always maps to the stable user ID for every scenario in ZITADEL (1) . If you need to associate your app user with a ZITADEL user, refer to the user’s unique ID as shown in the ZITADEL Console, but the mapping to sub in all cases is not explicitly documented. ​ To learn more, see the sources I considered: [1] OpenID Connect Endpoints in ZITADEL ​ Feel free to tag @Ask Inkeep with additional questions. Got it. Tagging @Rajat for additional help.
Rajat
Rajat19h ago
hey @Angel I will look into this tomorrow and will get back to you, thanks
Angel
AngelOP18h ago
GitHub
examples-api-access-and-token-introspection/api-jwt at main · zita...
Python examples for securing an API and invoking it as a service user - zitadel/examples-api-access-and-token-introspection
Ask Inkeep
Ask Inkeep18h ago
Glad I could be helpful. Feel free to create a new thread with any new questions you may have.

Did you find this page helpful?