Skip to main content

GET /clients/:id/evaluate

Requires manage-clients and view-users, since what it answers is the subject's data; without the second it is refused with 403 and a capability.refused row. The claims an authorization-code exchange for the client and subject would put in the ID token, the access token and the UserInfo response: scope resolved against the client's assignments as the exchange resolves it, the subject's roles narrowed to what that scope reaches, each artefact gated by its own scope flags — computed by the same functions the exchange and /userinfo call (mappedClaims and idTokenScopeOf, @odudu/protocol-oidc), never a copy of them. Mapped claims only: none of the envelope a signer adds (iss, aud, exp, …), and nothing is signed. id_token is null without openid; scope absent means the client's default scopes. It assumes every scope asked for is consented to: for a client with consent_required, a real code carries only the scopes the subject has consented to, which may be fewer. scope is at most 2048 characters. Each evaluation is audited as client.evaluate. An unknown client or subject answers 404.

For grace, her profile given its name claims through PATCH /subjects/:id/profile beforehand; once naming a scope, once not, and the row it left:

curl -sS -H "Authorization: Bearer $ADMIN_TOKEN" \
"$P/clients/01a0ee8a-d1a7-7c3c-a05b-903a2d8fa123/evaluate?subject=01a0ee8a-c58a-716c-96a6-edcab4ca1a70&scope=openid%20profile%20email%20roles"; echo
curl -sS -H "Authorization: Bearer $ADMIN_TOKEN" \
"$P/clients/01a0ee8a-d1a7-7c3c-a05b-903a2d8fa123/evaluate?subject=01a0ee8a-c58a-716c-96a6-edcab4ca1a70"; echo
curl -sS -H "Authorization: Bearer $ADMIN_TOKEN" \
"$P/audit?action=client.evaluate&limit=1"; echo
{"scope":"openid profile email roles","id_token":{"sub":"01a0ee8a-c58a-716c-96a6-edcab4ca1a70","name":"Grace Hopper","given_name":"Grace","family_name":"Hopper","preferred_username":"grace","updated_at":1790708493,"email":"grace@navy.example","email_verified":false},"access_token":{},"userinfo":{"sub":"01a0ee8a-c58a-716c-96a6-edcab4ca1a70","name":"Grace Hopper","given_name":"Grace","family_name":"Hopper","preferred_username":"grace","updated_at":1790708493,"email":"grace@navy.example","email_verified":false}}
{"scope":"openid profile email address phone roles groups","id_token":{"sub":"01a0ee8a-c58a-716c-96a6-edcab4ca1a70","name":"Grace Hopper","given_name":"Grace","family_name":"Hopper","preferred_username":"grace","updated_at":1790708493,"email":"grace@navy.example","email_verified":false},"access_token":{"groups":["/finance/payables"]},"userinfo":{"sub":"01a0ee8a-c58a-716c-96a6-edcab4ca1a70","name":"Grace Hopper","given_name":"Grace","family_name":"Hopper","preferred_username":"grace","updated_at":1790708493,"email":"grace@navy.example","email_verified":false,"groups":["/finance/payables"]}}
{"items":[{"id":"01a0ee8b-36e3-7657-8294-ea4e0498f059","occurred_at":"2026-09-29T19:01:36.085Z","event_type":"admin_mutation","action":"client.evaluate","outcome":"allowed","actor_tenant_id":"0199aa00-0000-7000-8000-000000000001","actor_subject_id":"01a0ee8a-bfb8-763c-ac22-0c8d97b0fada","actor_client_id":"01a0ee8a-bf72-77c9-a423-f61084924d9f","actor_name":null,"actor_origin":"system","resource_type":"client","resource_id":"01a0ee8a-d1a7-7c3c-a05b-903a2d8fa123","request_id":"01a0ee8b-36c5-7315-bb6f-d5c0b7732ca2","ip":"172.22.0.1","detail":{"scope":"openid profile email address phone roles groups","subject_id":"01a0ee8a-c58a-716c-96a6-edcab4ca1a70"}}],"next":"eyJhZnRlciI6IjIwMjYtMDktMjlUMTk6MDE6MzYuMDg1WnwwMWEwZWU4Yi0zNmUzLTc2NTctODI5NC1lYTRlMDQ5OGYwNTkiLCJjb2xsZWN0aW9uIjoiYXVkaXQiLCJ0ZW5hbnRJZCI6IjAxYTBlZThhLWMzOTQtN2I0OS05NDJiLTNkZWE3MGE5NjJlNyIsImZpbHRlcnMiOiI1V1p5R0tkUVJIX3VEUTNNRkpGclFtYzFDdWt4UllCcV9XSDhFc1dWd1ZBIn0.L9X-Z6lPk9dhSI-ZVeSTnwzC4owFeLuOMZdoJGBw-RI"}

Neither answer carries roles, though grace holds two and asked for the scope: ops-app is not full-scope, and the roles scope maps neither of hers, so a real token would carry none either. groups reaches the access token and UserInfo but not the ID token, whose include_in_id_token it has off.