---
kind: guide
zz: "0.8.0"
updated: 2026-10-10
state: live
rules: https://zriz.io/llms.txt
---
# 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.

## The problem {#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 {#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](https://zriz.io/docs.md) gives the commands. Add three actions to the resource file.

`.zriz/resources/target.json`:

```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`:

```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 {#run}

```sh
zz run order-owner-only
```

```text
"status":"pass"
```

## What it proves {#proves}

- The call of user A, `status == 200`: the owner can read the order. Thus the `404` of user B is not a wrong path.
- The call of user B, `status == 404`: a different user gets no access. This shop answers `404`, so user B cannot learn that the order exists. Your API can answer `403`.
- `body.order_id not-exists`: the refused result has no order data.

## Next {#next}

- [Test that duplicate requests create one row](https://zriz.io/learn/test-duplicate-requests-create-one-row.md)
- [Test that an API returns 401 without a token](https://zriz.io/learn/test-api-401-without-token.md)
