Back to all resources
KBAC KBAC Python

KBAC: Step-Up Authentication Advice in Authorization Responses

Demonstrates authZEN 'advice' - when authorization is denied due to insufficient authentication level, the response includes guidance on what authentication step-up is needed to gain access.

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.

ikg

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.

POST https://eu.api.indykite.com/capture/v1/nodes/Json
{
  "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.

POST https://eu.api.indykite.com/capture/v1/relationships/Json
{
  "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.

policy.jsonJson
{
  "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.

POST https://eu.api.indykite.com/configs/v1/authorization-policiesJson
{
  "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.

policy_request.pyPython

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.

GET https://eu.api.indykite.com/configs/v1/authorization-policies/{id}Json
{
  "id": "your_policy_configuration_gid"
}

Request to read the KBAC Policy configuration using REST.

policy_request.pyPython

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.

POST /access/v1/evaluationsJson
{
  "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.

evaluations.pyPython

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.

Response 200Json
{
  "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.

POST https://eu.api.indykite.com/access/v1/evaluationsJson
{
  "subject": {
    "type": "Token",
    "id": "token_sub_value"
  },
  "resource": {
    "type": "Laptop",
    "id": "airbook-xyz"
  },
  "action": {
    "name": "CAN_SERVICE"
  }
}

Response to the KBAC authZEN Evaluation request.

Response 200Json
{
  "decision": true
}

Step 5

Delete the KBAC Policies.

DELETE https://eu.api.indykite.com/configs/v1/authorization-policies/{id}Json
{
  "id": "your_policy_configuration_gid"
}

Request to delete the KBAC Policies.

del.pyPython

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
View OpenAPI docs

Tags

KBAC Policy authZEN Step-Up Authentication Advice MFA Authentication Level Conditional Access