What is AuthZEN?
AuthZEN is the OpenID Foundation standard for asking an authorization service a question of the form "may this subject perform this action on this resource?" and getting a yes/no answer. The application that asks is the Policy Enforcement Point (PEP); the service that answers is the Policy Decision Point (PDP). IndyKite is the PDP: it answers from KBAC policies evaluated against the IndyKite Knowledge Graph (IKG). Policies are written through the Config API (the Policy Administration Point) and the graph is the data the decision is made on (the Policy Information Point). The picture above shows these four roles.
AuthZEN does for authorization what OpenID Connect did for authentication: it replaces vendor-specific authorization APIs with one request and response format, so any application can ask any AuthZEN-compliant PDP and switch or combine PDPs without rewriting the calls. Every request is built from four entities:
subject: who is asking (a user, a service, an agent).action: what they want to do.resource: what they want to do it to.context: anything else the decision needs, such as a purchase amount or a region.
Externalizing authorization this way keeps the rules out of application code: they are managed, updated and audited in one place, and a fine-grained question such as "may this person drive this car right now?" is answered per request instead of being approximated by static roles. What IndyKite adds is the answer itself: the decision comes from relationships in the graph, so it changes as the data changes, without redeploying the application.
Terms used in this guide
| Term | Meaning |
| IKG | The IndyKite Knowledge Graph: a property graph of nodes and relationships, filled through the Capture API (see the Data Schema guide). |
| KBAC | Knowledge-Based Access Control: a policy is a graph pattern (Cypher) between a subject and a resource; the decision is true when the pattern matches (see the Dynamic Authorization guide). |
| KBAC policy | A configuration object with a subject type, a list of actions, a resource type and a condition. Created with POST /configs/v1/authorization-policies and Service Account credentials, or with Terraform indykite_authorization_policy. Versions 2.0-kbac and 3.0-kbac are documented here. |
| Application Agent (AppAgent) | The credential your application calls the runtime APIs with, sent as X-IK-ClientKey. It needs the Authorization API permission for the decision and search endpoints (see the Credentials guide). |
| User token | The end user's OAuth 2.0 access token, sent as Authorization: Bearer. Optional on every AuthZEN endpoint. A Token Introspect configuration validates it and maps its subject to a node. |
| Delegation token | An optional token in X-IK-Token, minted by the IndyKite Token Service, describing the chain of agents acting for a user. |
| Identity node | A node ingested with is_identity: true. Required for the subject of a 2.0-kbac policy. |
| Input parameter | A value supplied under context.input_params and read by a policy as $name. |
| Composite IKG | A graph split across locations for data residency; only relevant to 3.0-kbac routing (see the Data Residency guide). |
A complete example
The graph below is the one the AuthZEN resources of this library ingest. People drive cars, and a person holds a ticket for a bus.
Example graph: Person(knightrider) -DRIVES-> Car(kitt); Person(satchmo) -DRIVES-> Car(cadillacv16); Person(alice) -DRIVES-> Car(cadillacv16); Person(karel) -HAS-> Ticket(listek) -FOR-> Bus(harmonika)
Goal: the application asks whether knightrider may drive kitt. Four steps: ingest, policy, request, response.
Step 1: Ingest the graph
Nodes and relationships go in through the Capture API with AppAgent credentials. The people are identity nodes. The full payload is in resource authz-1; the relevant part:
POST /capture/v1/nodes
{
"nodes": [
{
"external_id": "knightrider",
"type": "Person",
"is_identity": true,
"properties": [
{
"type": "email",
"value": "knightrider@demo.com"
}
]
},
{
"external_id": "kitt",
"type": "Car",
"properties": [
{
"type": "manufacturer",
"value": "pontiac"
}
]
}
]
}
POST /capture/v1/relationships
{
"relationships": [
{
"source": {
"external_id": "knightrider",
"type": "Person"
},
"target": {
"external_id": "kitt",
"type": "Car"
},
"type": "DRIVES"
}
]
}
Step 2: Write the policy
A KBAC policy names a subject type, the actions it grants, a resource type, and the graph pattern that must exist between the two. In the pattern, the variable subject is the requesting subject and resource is the requested resource; the platform binds both from the request.
{
"meta": {
"policy_version": "2.0-kbac"
},
"subject": {
"type": "Person"
},
"actions": [
"CAN_DRIVE"
],
"resource": {
"type": "Car"
},
"condition": {
"cypher": "MATCH (subject:Person)-[:DRIVES]->(resource:Car)"
}
}
Store it through the Config API (Service Account credentials). The policy JSON travels as a string in the policy field:
POST /configs/v1/authorization-policies
Authorization: Bearer <service-account-token>
{
"project_id": "gid:AAAABbbbCCCC...",
"name": "person-can-drive-car",
"display_name": "A person can drive the cars they drive",
"policy": "{\"meta\":{\"policy_version\":\"2.0-kbac\"},\"subject\":{\"type\":\"Person\"},\"actions\":[\"CAN_DRIVE\"],\"resource\":{\"type\":\"Car\"},\"condition\":{\"cypher\":\"MATCH (subject:Person)-[:DRIVES]->(resource:Car)\"}}",
"status": "ACTIVE",
"tags": []
}
Step 3: Ask for the decision
The application calls the evaluation endpoint with its AppAgent credential. No user token is needed: the subject is identified by subject.type and subject.id, which are the node's type and external_id.
curl -X POST https://eu.api.indykite.com/access/v1/evaluation \
-H "X-IK-ClientKey: $CLIENT_KEY" \
-H "Content-Type: application/json" \
-d '{
"subject": {
"type": "Person",
"id": "knightrider"
},
"resource": {
"type": "Car",
"id": "kitt"
},
"action": {
"name": "CAN_DRIVE"
}
}'
Step 4: Read the response
{
"decision": true
}
The same request for "id": "cadillacv16" returns {"decision": false}: knightrider has no DRIVES relationship with that car. A denial carries nothing else unless a policy filter attached advice (see advice).
Endpoint reference
All endpoints are under https://eu.api.indykite.com or https://us.api.indykite.com, take Content-Type: application/json, and authenticate with X-IK-ClientKey. Authorization: Bearer and X-IK-Token are optional everywhere (see Tokens).
| Endpoint | Question | Answer |
POST /access/v1/evaluation | May this subject perform this action on this resource? | decision (boolean) |
POST /access/v1/evaluations | Several such questions in one call | One decision per question, in order |
POST /access/v1/search/subject | Which subjects of a type may perform this action on this resource? | List of subjects |
POST /access/v1/search/resource | On which resources of a type may this subject perform this action? | List of resources |
POST /access/v1/search/action | Which actions may this subject perform on this resource? | List of action names |
GET /access/v1/policies | Which active KBAC policies does my project have? | The policies as stored, with their tags |
POST /access/v1/evaluation
{
"subject": {
"type": "<node type>",
"id": "<node external_id>"
},
"resource": {
"type": "<node type>",
"id": "<node external_id>"
},
"action": {
"name": "<action>"
},
"context": {
"input_params": {
"<name>": <any>
},
"policy_tags": [
"<tag>"
]
}
}
| Field | Required | Rules |
subject.type, resource.type | yes | A node type: 2 to 64 characters, a letter or underscore followed by letters, digits or underscores. Selects the policies whose subject.type and resource.type match. |
subject.id, resource.id | yes | The node's external_id: 1 to 256 characters. |
action.name | yes | 2 to 50 characters from letters, digits, ., :, _ and /; no hyphen. Selects the policies whose actions list contains it, compared exactly and case-sensitively. It is not looked up in the graph. |
context.input_params | when a policy reads $name | Keys: 2 to 20 characters, a letter followed by letters, digits or underscores. String values: 1 to 256 characters. Numbers, booleans, arrays and objects are accepted. Every selected policy must find every parameter it references. |
context.policy_tags | no | Each tag 1 to 20 alphanumeric characters. When present, only policies carrying at least one of the tags are evaluated; policies without tags are left out. |
Other fields, including subject.properties, other keys under context, and options, are ignored without an error.
Response. Allowed:
{
"decision": true
}
Denied, when a policy's graph pattern matched but its filter attached advice:
{
"decision": false,
"context": {
"advice": [
{
"error": "insufficient_user_authentication",
"error_description": "Authentication is missing or expired",
"sub": "$token.exp"
}
]
}
}
Denied without advice is {"decision": false}. context is absent when there is nothing to report. The single evaluation never returns a reason: anything that prevents a decision is an HTTP error (see Errors).
POST /access/v1/evaluations
Several evaluations in one call, which saves a round trip per question when one subject is checked against many resources or one resource against many actions. Top-level subject, resource, action and context are defaults; each entry of evaluations supplies what differs.
{
"subject": {
"type": "Person",
"id": "karel"
},
"action": {
"name": "CAN_DRIVE"
},
"evaluations": [
{
"subject": {
"type": "Person",
"id": "knightrider"
},
"resource": {
"type": "Car",
"id": "kitt"
}
},
{
"subject": {
"type": "Person",
"id": "knightrider"
},
"resource": {
"type": "Car",
"id": "cadillacv16"
}
},
{
"resource": {
"type": "Bus",
"id": "harmonika"
},
"action": {
"name": "CAN_RIDE"
}
},
{
"resource": {
"type": "Bus",
"id": "harmonika"
}
}
]
}
{
"evaluations": [
{
"decision": true
},
{
"decision": false
},
{
"decision": true
},
{
"decision": false
}
]
}
Rules:
- A default is replaced, not merged. An entry that sets
contextuses only its owninput_paramsandpolicy_tags; the default context is not consulted for that entry. The same holds forsubject,resourceandaction. In the example, the third entry keeps the default subject karel and overrides the action; the fourth keeps both defaults and asks whether karel may drive a bus, which no policy grants. - Results come back in request order, one per entry, each with the same shape as a single evaluation response.
- One failing entry does not fail the call. The call returns
200; the entry gets"decision": falseandcontext.reasonexplains it, for example"reason": "invalid_argument: missing or wrong input params, 'max_price'". The same situations are HTTP errors on the single endpoint. - Field validation still applies to the merged entries. An entry whose merged subject, resource or action is missing or malformed is refused for the whole call with
400and the field messages listed undererrors.
The three search endpoints
A search leaves one side of the question open and returns every graph node, or every action, that would make the decision true. The open side gives only a type. Results are deduplicated and there is no paging.
| Endpoint | Request body | Response |
POST /access/v1/search/subject | subject.type, action.name, resource.type, resource.id, optional context | {"results": [{"type": "...", "id": "..."}]} |
POST /access/v1/search/resource | subject.type, subject.id, action.name, resource.type, optional context | {"results": [{"type": "...", "id": "..."}]} |
POST /access/v1/search/action | subject.type, subject.id, resource.type, resource.id, optional context | {"results": [{"name": "..."}]} |
The three searches on the example graph, all with X-IK-ClientKey only. "Who may drive kitt?":
POST /access/v1/search/subject
{
"subject": {
"type": "Person"
},
"action": {
"name": "CAN_DRIVE"
},
"resource": {
"type": "Car",
"id": "kitt"
}
}
{
"results": [
{
"type": "Person",
"id": "knightrider"
}
]
}
"Which cars may knightrider drive?":
POST /access/v1/search/resource
{
"subject": {
"type": "Person",
"id": "knightrider"
},
"action": {
"name": "CAN_DRIVE"
},
"resource": {
"type": "Car"
}
}
{
"results": [
{
"type": "Car",
"id": "kitt"
}
]
}
"What may knightrider do with kitt?":
POST /access/v1/search/action
{
"subject": {
"type": "Person",
"id": "knightrider"
},
"resource": {
"type": "Car",
"id": "kitt"
}
}
{
"results": [
{
"name": "CAN_DRIVE"
}
]
}
Only the CAN_DRIVE policy exists at this point, so the action search returns one name. With input parameters, add context.input_params to any of the three bodies exactly as on the evaluation (resource authz-4 does this for a resource and a subject search).
Two differences from the evaluation:
- Subject search uses no tokens and no filter. The user token and the delegation token are not read, and a policy's
condition.filteris not evaluated. A policy whose filter or Cypher reads a token claim therefore matches no subject in this search; resource and action searches do evaluate the filter and bind the claims. - An empty result is not an error. No matching policy, or no matching node, gives
{"results": []}.
Full examples: resource authz-1.
GET /access/v1/policies
An application agent can list the active KBAC policies of its own project at runtime, for example to show an administrator which rules apply, to pick policy_tags, or to let an agent discover which actions and resource types exist for a subject type. The agent needs the ReadAuthZConfigs API permission, which is separate from Authorization and is not granted to existing agents automatically (see the Environment guide).
curl https://eu.api.indykite.com/access/v1/policies?subject_type=Person \
-H "X-IK-ClientKey: $CLIENT_KEY"
{
"results": [
{
"policy": {
"meta": {
"policy_version": "2.0-kbac"
},
"subject": {
"type": "Person"
},
"actions": [
"CAN_DRIVE"
],
"resource": {
"type": "Car"
},
"condition": {
"cypher": "MATCH (subject:Person)-[:DRIVES]->(resource:Car)"
}
},
"tags": []
},
{
"policy": {
"meta": {
"policy_version": "2.0-kbac"
},
"subject": {
"type": "Person"
},
"actions": [
"CAN_RIDE"
],
"resource": {
"type": "Bus"
},
"condition": {
"cypher": "MATCH (subject)-[:HAS]->(ticket:Ticket)-[:FOR]->(resource)"
}
},
"tags": []
}
]
}
subject_type(optional query parameter, a valid node type of 2 to 64 characters) keeps only the policies with thatsubject.type. Omitted: every policy. A type no policy uses returns{"results": []}; a value that is not a valid node type is refused with422.results[].policyis thepolicystring sent to the Config API, parsed into an object, exactly as authored (includingfilterwhen present).results[].tagsare the values matched bycontext.policy_tags;[]when the policy has none.- Only KBAC policies with status
ACTIVE, only the calling agent's project. ContX IQ policies are not listed. Policy IDs, names and timestamps are not returned; use the Config API to manage a policy. - A user token or delegation token sent on this call is still validated and can fail with
401; it has no other effect.
Full example: resource authz-9.
Credentials and tokens
| Header | Required | Effect |
X-IK-ClientKey | always | The AppAgent credential. Authorization permission for decisions and searches, ReadAuthZConfigs for the policy listing. |
Authorization: Bearer <user token> | optional | Validated by Token Introspect. Its claims become $token.<claim> in policies. On 2.0-kbac the subject must also be the token's identity, otherwise the decision is false without an error. On 3.0-kbac the token's subject must equal subject.type and subject.id, otherwise 403. Not read by the subject search. |
X-IK-Token: <delegation token> | optional | Raw token, no Bearer prefix. Validated by Token Introspect like the user token. Its claims become $ik_token.<claim>. Does not require a user token. When a user token with a sub claim is also present, both sub claims must be equal. |
To see which subject.type and subject.id a user token resolves to, call GET /contx-iq/v1/whoami (see the ContX IQ guide). The subject is the same for both APIs.
The delegation token carries the chain of agents acting for the user: $ik_token.sub is the user, $ik_token.act.sub the agent that made this call, $ik_token.act.act.sub the one before it, and so on. The Token Service sets a type on the link it adds (Agent unless configured otherwise) and keeps the links of the incoming token unchanged. A policy reads the chain through $ik_token.act... in its filter or its Cypher.
How a decision is computed
- Policies are selected by exact match of
subject.type,resource.typeandaction.nameagainst each active policy'ssubject.type,resource.typeandactions, and bypolicy_tagswhen sent. No policy selected:{"decision": false}, not an error. - Input parameters are bound. Every selected policy must find every
$nameits Cypher references incontext.input_params, even if another policy would already allow. A missing one fails the request with422andmissing or wrong input params, '<name>'(several names are comma-separated). - Each policy's Cypher condition runs with
subjectandresourcebound to the requested nodes. The condition holds when the pattern matches. - Its filter runs, if the policy has one and the Cypher condition held. The filter is evaluated without the graph, against input parameters and token claims.
- Any policy that holds makes the decision
true. KBAC has no deny rule: a policy can only allow, and a filter can only stop its own policy from allowing. Advice is returned only when the decision isfalse.
An error inside any selected policy, such as a missing parameter or a runtime failure, fails the whole decision on the single endpoint and marks that entry with a reason in a batch.
Advice
A filter leaf may carry an advice object: a free map of string keys and values written by the policy author. When that leaf fails, the graph condition had matched, and no other policy allows, the map is returned under context.advice. There is no fixed set of keys; error and error_description are a convention borrowed from OAuth. This policy denies a service action on a laptop when the user token is older than a moment fixed in the policy, and says why:
{
"meta": {
"policy_version": "2.0-kbac"
},
"subject": {
"type": "Token"
},
"actions": [
"CAN_SERVICE"
],
"resource": {
"type": "Laptop"
},
"condition": {
"cypher": "MATCH (subject:Token)-[:_SAME_AS]->(person:Person)-[:OWNS]->(resource)",
"filter": {
"attribute": "$token.exp",
"operator": ">",
"value": 1767225600,
"advice": {
"error": "insufficient_user_authentication",
"error_description": "Authentication is missing or expired",
"sub": "$token.exp"
}
}
}
}
Full example: resource authz-3. With AND, the advice of every failing operand is returned; with OR, none is returned when one operand succeeds.
KBAC policy reference
The policy JSON is the same for both versions; only meta.policy_version and the rules below differ. The Cypher guide covers how to write the pattern; the Data Residency guide covers routing on a composite IKG.
{
"meta": {
"policy_version": "2.0-kbac | 3.0-kbac"
},
"subject": {
"type": "<node type>"
},
"actions": [
"<action>"
],
"resource": {
"type": "<node type>"
},
"condition": {
"cypher": "<Cypher pattern between subject and resource>",
"filter": {
"operator": "<op>",
"attribute": "<$token.claim | $ik_token.path | $param>",
"value": <any>,
"operands": [],
"advice": {
"<key>": "<string>"
}
}
}
}
2.0-kbac and 3.0-kbac
| Aspect | 2.0-kbac |
3.0-kbac |
| Node labels | The subject must be an identity node; the platform matches subject as an identity node of the subject type and every other node pattern as an ingested node. A subject ingested without is_identity: true never matches, so the decision is false with no error. | The Cypher runs as written; the platform only pins subject and resource by type and external_id. A node pattern without a label matches any node, including property and trust score nodes. is_identity is not required. |
| User token | Optional. When sent, the subject must be the token's identity; otherwise false. | Optional. When sent, its subject must equal the requested subject; otherwise 403 with bearer token subject differs from requested subject (evaluation, evaluations, resource and action search). |
USE graph.byName(...) and CALL { } | Rejected at creation: USE clause is not allowed, CALL { } subquery is not allowed. | Allowed. Only a USE clause needs a composite IKG; a 3.0-kbac policy without one runs on the default database. A top-level RETURN is still rejected. |
| Resolver-backed (external) properties in the condition | Allowed. | Rejected at creation: invalid policy config: external properties cannot be used in data-residency policies. |
$subject_id | May be referenced, but then counts as an input parameter the request must supply. | Rejected at creation: invalid policy config: parameter "$subject_id" is reserved and cannot be referenced. |
Platform-bound parameters $subject_external_id, $subject_type, $resource_external_id, $resource_type | Bound by the platform. Do not reference them: a policy that does turns them into required input parameters. | Reserved: referencing them is rejected at creation. |
A 2.0-kbac condition is accepted as a 3.0-kbac condition when it uses none of the rejected items, but it can decide differently: the labels the platform no longer adds, and the token check that becomes an error, are behaviour changes, not just schema changes. Policies with the older 1.0-kbac version keep working and appear in the listing; this guide does not describe them.
condition.filter
A boolean tree evaluated without the graph, after the Cypher condition held. Attributes are token claims or input parameters; the graph is not visible to it.
| Element | Rule |
operator | AND, OR (two or more operands; one gives invalid field operands: len is 1 and should be >= 2), NOT (exactly one operand), =, <>, <, <=, >, >=, IN, =~, STARTS WITH, ENDS WITH, CONTAINS, IS NULL, IS NOT NULL. operands only with AND, OR, NOT. |
attribute | Required on a leaf, a string: $token.<claim>, $ik_token.<path> (dot paths walk the act chain, e.g. $ik_token.act.act.sub), $name or $name.path for an input parameter. |
value | Required for every comparison, must be absent for IS NULL and IS NOT NULL. A literal, a $ reference as above, an array for IN, or a typed object {"type": "datetime", "value": "<RFC 3339 or $param>"}. |
CONTAINS | The attribute is a whitespace-separated list that contains the value as one of its items, as in an OAuth scope claim. Comma-separated lists and case differences do not match; a non-string attribute gives false. This differs from Cypher's substring CONTAINS. |
IS NULL | True for a token claim that is absent, including when the token itself was not sent. A referenced input parameter must exist; IS NULL is true when its value is null. |
advice | A map of string keys and values on a leaf; returned as described under Advice. |
| Unknown fields | Rejected at creation. |
Example, a read that also requires the cars.read scope in the user token (resource authz-6):
"condition": {
"cypher": "MATCH (subject:Person)-[:DRIVES]->(resource:Car)",
"filter": {
"operator": "CONTAINS",
"attribute": "$token.scope",
"value": "cars.read"
}
}
Token claims in the Cypher condition
The same claim sets are bound as Cypher parameters, so the pattern itself can compare graph data with a claim: $token.<claim> and $ik_token.<path>, on both versions and on every endpoint except the subject search.
{
"meta": {
"policy_version": "2.0-kbac"
},
"subject": {
"type": "Person"
},
"actions": [
"CAN_DRIVE"
],
"resource": {
"type": "Car"
},
"condition": {
"cypher": "MATCH (subject:Person)-[:OWNS]->(resource:Car) WHERE resource.delegated_to = $ik_token.act.sub AND resource.driver = $token.sub"
}
}
tokenandik_tokenare reserved names. They are never input parameters: the platform binds them from the request's tokens and replaces anything the caller sends under those names.- A token that was not sent binds an empty claim set. A comparison such as
= $ik_token.act.subis then false and the decision isfalse, not an error;$ik_token.act.sub IS NULLis true. - Only the exact names are reserved. A misspelt
$iktoken.act.subis an ordinary input parameter, refused with422andmissing or wrong input params, 'iktoken'. - A claim cannot route a query:
USE graph.byName($token.region)is rejected at creation (see below).
Location routing (3.0-kbac on a composite IKG)
A 3.0-kbac policy may start with USE graph.byName(...), either a string literal or a single parameter such as $region. The request supplies the logical location, a key of the project's alias_mapping, as an ordinary input parameter:
{
"subject": {
"type": "Person",
"id": "knightrider"
},
"resource": {
"type": "Car",
"id": "kitt"
},
"action": {
"name": "CAN_DRIVE"
},
"context": {
"input_params": {
"region": "east"
}
}
}
This works identically on all decision and search endpoints. Rules and their exact messages:
- The routing parameter is used nowhere else in the Cypher:
invalid policy config: parameter "$region" routes graph.byName() and cannot be referenced elsewhere. - The argument is a literal or one bare parameter:
invalid policy config: graph.byName() argument must be a string literal or a single parameter. This is also what$token.regiongets. - It cannot be a platform-bound name:
invalid policy config: graph.byName() parameter "$subject_external_id" collides with a platform-bound parameter, and for the claim setsinvalid policy config: graph.byName() cannot take "$token", the platform binds it to the token claims. - At request time,
422:location parameter "$region" must be a non-empty string;unknown location "mars" for parameter "$region";location parameter "$region" requires a composite database, but the app space has none configured; and for a staticUSEwithout a composite IKG,policy requires a composite database, but the app space has none configured.
Full examples: resources authz-7 and authz-8.
Errors
| Status | Body and cause |
| 400 | {"message": "Bad Request"}: the body is not valid JSON. With "errors": ["invalid field <path>"]: a field has the wrong JSON type. In a batch, an entry whose merged fields fail validation is reported here with the field messages. |
| 401 | {"message": "Missing or malformed AppAgent credential token in X-IK-ClientKey header"} or {"message": "Invalid AppAgent JWT in X-IK-ClientKey header"}: the AppAgent credential. {"message": "Invalid token in Authorization header"} or {"message": "Invalid token in X-IK-Token header"}: that token is expired, not accepted by any Token Introspect configuration, sent with a Bearer prefix in X-IK-Token, or could not be introspected right now; in the last case a retry can succeed. {"message": "Token in X-IK-Token header is missing the sub claim"} and {"message": "Token in X-IK-Token header carries a different sub than the subject token"}: the two tokens are not about the same user. {"message": "insufficient API access level for appAgent"}: the agent lacks the permission the endpoint needs. |
| 403 | {"message": "Forbidden", "errors": ["bearer token subject differs from requested subject"]}: a 3.0-kbac policy was selected and the user token's subject is not the requested subject. |
| 406 | {"error": "Accept must be application/json or */*", ...}: the Accept header excludes JSON. |
| 415 | {"error": "Content-Type must be application/json", ...}: missing or other content type. |
| 422 | {"message": "Unprocessable Entity", "errors": [...]}. Field validation: missing field subject, missing field subject.type, invalid field subject.type: "A" does not meet rule min=2, invalid field subject.type: does not match regex "^[a-zA-Z_]\w+$", invalid field context.input_params[key]: string "..." must not be shorter than 1 and longer than 256 characters. Decision time: missing or wrong input params, '<name>' and the location messages above. |
| 503 | {"message": "Unable to verify the AppAgent credential token, retry the request"}: the AppAgent credential could not be checked right now. Retry with the same credential. |
What is IndyKite-specific compared to the AuthZEN specification
- Additional inputs go under
context.input_paramsandcontext.policy_tags; no othercontextkey is read. subject.properties,resource.properties,action.propertiesandoptions(includingevaluations_semantic) are accepted and ignored. Every batch entry is evaluated; there is no short-circuit semantic.subject.idandresource.idare node external_ids,subject.typeandresource.typeare node types, andaction.nameis matched against policy action lists, never against the graph.- The searches return complete, deduplicated result lists without paging, and the subject search evaluates no tokens and no filter.
- The response adds
context.advice(policy-authored maps) and, in batches,context.reason. - Credentials are IndyKite headers:
X-IK-ClientKey, optionalAuthorization: Bearer, optionalX-IK-Token.
Related
- Resources: authz-1 (evaluation and the three searches), authz-2 (batch), authz-3 (advice), authz-4 (input parameters), authz-5 (permission grid), authz-6 (token scope), authz-7 and authz-8 (3.0-kbac), authz-9 (policy listing).
- Guides: Dynamic Authorization, Cypher for policies, Data Residency, Token Introspect, Token Service, Credentials.
- OpenID AuthZEN specification: https://openid.net/wg/authzen/
