zrizDocsLearnSecuritySign inSign up

Test API authorization between two users

Make two users in one zriz pipeline. User A creates an order, then user B asks for it. The API must refuse user B: this finds broken access control.

Lesson 4 of 10 · zz 0.8.0 or later · Updated · Plain text for agents: /learn/test-api-authorization-between-users.md

The problem

A token proves who the user is, not that the record is theirs. If the API reads an order by id with no owner check, each user can read each order. The name of this fault is IDOR, or broken access control.

The test

The example is a shop API. GET /api/orders/{order-id} gives one order. The value of order-id in ctx fills the path.

Start from a project that zz init made: the quick start gives the commands. Add three actions to the resource file.

.zriz/resources/target.json:

{
  "type": "http",
  "description": "Your app under test",
  "base-url": "${env.TARGET_URL}",
  "headers": { "Content-Type": "application/json" },
  "actions": {
    "check": { "method": "GET", "path": "/api/health" },
    "register": { "method": "POST", "path": "/api/auth/register" },
    "create-order": { "method": "POST", "path": "/api/orders" },
    "get-order": { "method": "GET", "path": "/api/orders/{order-id}" }
  }
}

.zriz/pipelines/order-owner-only.json:

{
  "description": "User B must not read the order of user A",
  "steps": [
    {
      "call": "target/register",
      "body": { "email": "a-${gen.uuid}@test.com", "password": "secret123", "name": "Ann" },
      "expect": [["status", "==", 201]],
      "save": { "token-a": "body.token" }
    },
    {
      "call": "target/register",
      "body": { "email": "b-${gen.uuid}@test.com", "password": "secret123", "name": "Bob" },
      "expect": [["status", "==", 201]],
      "save": { "token-b": "body.token" }
    },
    {
      "call": "target/create-order",
      "headers": { "Authorization": "Bearer ${ctx.token-a}" },
      "body": { "items": [{ "sku": "KB-001", "quantity": 1 }] },
      "expect": [["status", "==", 201]],
      "save": { "order-id": "body.order_id" }
    },
    {
      "call": "target/get-order",
      "headers": { "Authorization": "Bearer ${ctx.token-a}" },
      "expect": [["status", "==", 200], ["body.order_id", "==", "${ctx.order-id}"]]
    },
    {
      "call": "target/get-order",
      "headers": { "Authorization": "Bearer ${ctx.token-b}" },
      "expect": [["status", "==", 404], ["body.order_id", "not-exists"]]
    }
  ]
}

Run it

zz run order-owner-only
"status":"pass"

What it proves

Next