KBAC: Step-Up Authentication Advice in Authorization Responses
This example demonstrates authZEN "advice" responses for step-up authentication:
What is advice?
When authorization is denied, the response can include guidance on HOW to get access (not just "denied").
Example flow:
1. User tries to access sensitive resource with basic authentication
2. KBAC denies access but returns advice: "Requires MFA authentication"
3. Application prompts user for MFA
4. User re-authenticates with MFA
5. Same request now succeeds
This enables progressive authentication - users only need stronger auth for sensitive operations.
Use case
Scenario: A user wants to service their laptop (e.g., wipe data, change settings).
Policy conditions:
1. Person must OWN the Laptop (relationship check)
2. Person's authentication token must not be expired (time check)
3. Person's subject.external_id must match the token's "sub" claim (identity verification)
Without proper authentication:
- Request: "Can Alice service Laptop1?"
- Response: {decision: false, advice: {step_up: "mfa_required"}}
With proper authentication (MFA token):
- Request: "Can Alice service Laptop1?" (with valid MFA token)
- Response: {decision: true}
The advice object tells the application exactly what authentication upgrade is needed.

Requirements
Prerequisites:
- ServiceAccount credentials: For creating policies (Bearer token)
- AppAgent credentials: For data ingestion and authorization queries (X-IK-ClientKey)
- Auth0 (or similar) user token: For the user requiring access
Required API access:
- POST /capture/v1/nodes/ and /capture/v1/relationships/ (graph data)
- POST /configs/v1/authorization-policies (create policy)
- POST /access/v1/evaluation (authorization check with advice)
Steps
Step 1: Ingest Graph Data
- Authentication: AppAgent credential (X-IK-ClientKey header)
- Action: POST Person and Laptop nodes with OWNS relationship
- Result: Graph ready for authorization queries
Step 2: Create Step-Up Policy
- Authentication: ServiceAccount credential (Bearer token)
- Action: POST policy with conditions:
- Person must OWN the Laptop
- Token "sub" claim must match Person.external_id
- Token must not be expired
- Policy includes advice configuration for step-up scenarios
- Result: Policy ID returned
Step 3: Evaluation Without Proper Auth (Returns Advice)
- Authentication: AppAgent credential (X-IK-ClientKey header)
- Request: Evaluate "Can Alice service Laptop1?" without user token
- Response: {decision: false, advice: {action: "step_up", auth_level: "mfa"}}
- The advice tells the app to request MFA from the user
Step 4: Evaluation With Proper Auth (Grants Access)
- Authentication: AppAgent credential + User's MFA token (Bearer header)
- Request: Same evaluation request, but now with valid MFA token
- Response: {decision: true}
- User is now authorized because token claims satisfy policy conditions
Step 5: Cleanup
- Action: DELETE policy configuration
Step 1
Capture the nodes needed for this use case.
{
"nodes": [
{
"external_id": "alice",
"is_identity": true,
"type": "Person",
"properties": [
{
"type": "email",
"value": "alice@email.com"
},
{
"type": "given_name",
"value": "Alice"
},
{
"type": "last_name",
"value": "Smith"
}
]
},
{
"external_id": "knightrider",
"type": "Person",
"is_identity": true,
"properties": [
{
"type": "email",
"value": "knightrider@demo.com"
},
{
"type": "name",
"value": "Michael Knight"
}
]
},
{
"external_id": "satchmo",
"type": "Person",
"is_identity": true,
"properties": [
{
"type": "email",
"value": "satchmo@demo.com"
},
{
"type": "name",
"value": "Louis Armstrong"
}
]
},
{
"external_id": "karel",
"type": "Person",
"is_identity": true,
"properties": [
{
"type": "email",
"value": "karel@demo.com"
},
{
"type": "name",
"value": "Karel Plihal"
}
]
},
{
"external_id": "kitt",
"type": "Car",
"is_identity": false,
"properties": [
{
"type": "manufacturer",
"value": "pontiac"
},
{
"type": "model",
"value": "Firebird"
}
]
},
{
"external_id": "cadillacv16",
"type": "Car",
"is_identity": false,
"properties": [
{
"type": "manufacturer",
"value": "Cadillac"
},
{
"type": "model",
"value": "V-16"
}
]
},
{
"external_id": "harmonika",
"type": "Bus",
"is_identity": false,
"properties": [
{
"type": "manufacturer",
"value": "Ikarus"
},
{
"type": "model",
"value": "280"
}
]
},
{
"external_id": "listek",
"type": "Ticket",
"is_identity": false
},
{
"external_id": "airbook-xyz",
"type": "Laptop",
"is_identity": false
}
]
}Capture the relationships needed for this use case.
{
"relationships": [
{
"source": {
"external_id": "knightrider",
"type": "Person"
},
"target": {
"external_id": "kitt",
"type": "Car"
},
"type": "DRIVES"
},
{
"source": {
"external_id": "satchmo",
"type": "Person"
},
"target": {
"external_id": "cadillacv16",
"type": "Car"
},
"type": "DRIVES"
},
{
"source": {
"external_id": "karel",
"type": "Person"
},
"target": {
"external_id": "listek",
"type": "Ticket"
},
"type": "HAS"
},
{
"source": {
"external_id": "listek",
"type": "Ticket"
},
"target": {
"external_id": "harmonika",
"type": "Bus"
},
"type": "FOR"
},
{
"source": {
"external_id": "karel",
"type": "Person"
},
"target": {
"external_id": "airbook-xyz",
"type": "Laptop"
},
"type": "OWNS"
},
{
"source": {
"external_id": "alice",
"type": "Person"
},
"target": {
"external_id": "airbook-xyz",
"type": "Laptop"
},
"type": "OWNS"
},
{
"source": {
"external_id": "knightrider",
"type": "Person"
},
"target": {
"external_id": "kitt",
"type": "Car"
},
"type": "OWNS"
},
{
"source": {
"external_id": "alice",
"type": "Person"
},
"target": {
"external_id": "cadillacv16",
"type": "Car"
},
"type": "DRIVES"
}
]
}Step 2
KBAC Policy which rules that a Person node can service a laptop if the Person node owns it and if the external_id of the subject Person node is equal to the token sub.
{
"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": 1775490149,
"advice": {
"error": "insufficient_user_authentication",
"error_description": "Authentication is expired",
"sub": "$token.exp"
}
}
}
}Request to create the KBAC Policy configuration using REST.
{
"project_id": "your_project_gid",
"description": "description of policy",
"display_name": "policy name",
"name": "policy-name",
"policy": "{\"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\":1775490149,\"advice\":{\"error\":\"insufficient_user_authentication\",\"error_description\":\"Authentication is expired\",\"sub\":\"$token.exp\"}}}}",
"status": "ACTIVE",
"tags": []
}Request to create the KBAC Policy configuration using REST.
import http.client
conn = http.client.HTTPSConnection("eu.api.indykite.com")
payload = "{"description": "",
"display_name": "",
"name": "",
"policy": "",
"project_id": "",
"status": "ACTIVE",
"tags": [
""
]}"
headers = {
'Content-Type': "application/json",
'Authorization': "YOUR_SECRET_TOKEN"
}
conn.request("POST", "/configs/v1/authorization-policies", payload, headers)
res = conn.getresponse()
data = res.read()
Request to read the KBAC Policy configuration using REST.
{
"id": "your_policy_configuration_gid"
}Request to read the KBAC Policy configuration using REST.
import http.client
conn = http.client.HTTPSConnection("eu.api.indykite.com")
headers = { 'Authorization': "YOUR_SECRET_TOKEN" }
conn.request("GET", "/configs/v1/authorization-policies/{{id}}", headers=headers)
res = conn.getresponse()
data = res.read()
Step 3
Run a KBAC authZEN Evaluation which returns step up.
{
"subject": {
"type": "Token",
"id": "token_sub_value"
},
"resource": {
"type": "Laptop",
"id": "airbook-xyz"
},
"action": {
"name": "CAN_SERVICE"
}
}Request to run KBAC authZEN Evaluation which returns step up.
import http.client
conn = http.client.HTTPSConnection("eu.api.indykite.com")
payload = "{
"subject": {"type": "Token",
"id": "token_sub_value"},
"resource": {"type": "Laptop",
"id": "airbook-xyz"},
"action": {"name": "CAN_SERVICE"}
}"
headers = {
'Content-Type': "application/json",
'Authorization': "Bearer eyJh....",
'X-IK-ClientKey': "eyJh...."
}
conn.request("POST", "/access/v1/evaluation", payload, headers)
res = conn.getresponse()
data = res.read()
Response to the KBAC authZEN Evaluation request.
{
"context": {
"advice": [
{
"error": "insufficient_user_authentication",
"error_description": "Authentication is expired",
"sub": "$token.exp"
}
]
},
"decision": false
}Step 4
Add the Person node access token in the headers:
key:Authorization value: Bearer access_token_value
Then run the same KBAC authZEN Evaluation.
{
"subject": {
"type": "Token",
"id": "token_sub_value"
},
"resource": {
"type": "Laptop",
"id": "airbook-xyz"
},
"action": {
"name": "CAN_SERVICE"
}
}Response to the KBAC authZEN Evaluation request.
{
"decision": true
}Step 5
Delete the KBAC Policies.
{
"id": "your_policy_configuration_gid"
}Request to delete the KBAC Policies.
import http.client
conn = http.client.HTTPSConnection("eu.api.indykite.com")
headers = { 'Authorization': "Bearer ...", 'Content-Type': "application/json" }
conn.request("DELETE", "/configs/v1/authorization-policies/{id}", headers=headers)
res = conn.getresponse()
data = res.read()
API Endpoints
/capture/v1/nodes /capture/v1/relationships /configs/v1/authorization-policies /kbac/v1/is-authorized