Beta

Create a tenant

A new tenant starts with its own users, OAuth settings, audit history and logs. You are its first Tenant Admin.

BTL Admin

AUTHORIZATION AND POLICY · LAB

Object and field checks in the tenant's own APIs

See one permission give different answers for different users, a field that needs a stronger permission than its record, and lists that stay inside their scope.

Partly readyUses your lab tenant

The lesson

Builds on: Designing permissions.

New to the labs? Start with the lab toolkit and the shared cast and names every lab uses.

Partly ready. Most of this lab runs today. Steps that wait on platform features are marked, and Missing infrastructure says what they need.

Your progress

Press Start before you begin. Only events your tenant records after that count, in the order below. Checking reads your tenant's Audit, so you need Audit read access in it.

  1. Give Cora the Auditor role

    Recorded as tenant.users.management_roles.assign succeeded about [email protected].

  2. Ben edits a user who holds no roles

    Recorded as tenant.users.update succeeded about [email protected].

  3. Ben tries to edit a role holder

    Recorded as tenant.users.update rejected about [email protected].

  4. Ben tries to move Ava's email address

    Recorded as tenant.users.update rejected about [email protected].

Request console

Requests in this lab can be sent from this page to your tenant: open one and choose Send. Fill in the values below first. They stay in this page's memory and are gone when you leave; secrets are never stored or sent anywhere except the request you send.

Setup

Nested photos, moves, signed thumbnail links and search counts need the photo library API (G3, G22). The tenant's user directory makes the same kind of per-object and per-field decisions today, through the portal and through SCIM.

  1. Complete Read and use the tenant permission catalog first.

  2. In Roles, add tenant.users.update to Help desk. Create Auditor if it does not exist, with tenant.overview.read, tenant.audit.read, tenant.logs.read and tenant.roles.read.

  3. Open Users > Cora > Management roles and assign Auditor. Cora now holds a permission Ben lacks.

  4. In Provisioning, turn SCIM on. In the SCIM console at /docs/scim/, create lab-scim-reader with only the read scope scim-<id6>.read and lab-provisioning with the full scope scim-<id6>, if they do not exist. The <id6> part is the first six characters of your tenant ID.

  5. Note Ava's and Cora's user IDs from Users.

Walkthrough

  1. In Ben's window open $ISSUER/manage, then Users > Ava, and change her last name to Archer-Lab. It saves. Change it back.

Why it matters: Ben's permission applies to an object he may act on. The check is made against this user, not just against the Users page.

  1. As Ben, try the same edit on Cora. It is refused with "You do not have permission to make this change."

Why it matters: the decision depends on the object. Changing someone who holds management roles requires holding every permission their roles grant, so Ben cannot weaken an Auditor's account even though he may edit users in general.

  1. As Ben, try to change Ava's email address to [email protected]. It is refused, while her name change went through.

Why it matters: the email field receives password resets, so moving it needs tenant.users.credentials.set as well. This is the lesson's photo.edit_rights on the same record as the caption: one route, one record, different permissions per field.

  1. Responses built from what may be read. Get a read token and ask SCIM for two fields of Ava's record:

read -rs CLIENT_SECRET   # lab-scim-reader's secret
SCIM=$(curl -s -u "<lab-scim-reader client ID>:$CLIENT_SECRET" -d grant_type=client_credentials -d "scope=scim-<id6>.read" "$ISSUER/oauth/token" | jq -r .access_token)
GET$ISSUER/scim/v2/Users/<Ava's Open in console
GET $ISSUER/scim/v2/Users/<Ava's user ID>?attributes=userName,active HTTP/1.1
Authorization: Bearer $SCIM

Only those fields come back, with id and schemas. No request returns a password.

Why it matters: the response is built from an allowlist of readable fields. A write-only field never leaves, whatever the caller asks for, as the legal hold note stays out of every response that should not carry it.

  1. Lists stay inside the caller's scope. Ask for a page of one user:

GET$ISSUER/scim/v2/Users?count=1 Open in console
GET $ISSUER/scim/v2/Users?count=1 HTTP/1.1
Authorization: Bearer $SCIM

totalResults counts this tenant's users only. Optional: send the same request with the same token to $ISSUER2. It is refused with 401, because the token means nothing to Lab Mail.

Why it matters: totals and pages come from the caller's filtered set. Another tenant's users do not exist for this caller, so no count can reveal them.

  1. The same object rule through another entry point. Get a token for lab-provisioning with the scope scim-<id6> into SCIM, as in step 4, and try to deactivate Cora:

PATCH$ISSUER/scim/v2/Users/<Cora's Open in console
PATCH $ISSUER/scim/v2/Users/<Cora's user ID> HTTP/1.1
Authorization: Bearer $SCIM
Content-Type: application/scim+json

{"schemas":["urn:ietf:params:scim:api:messages:2.0:PatchOp"],"Operations":[{"op":"replace","path":"active","value":false}]}

It is refused with 403 and scimType protected_user.

Why it matters: the object rule holds on every route, not only behind the portal button. A provisioning client cannot change a person who holds management roles.

Break it

  1. A batch with per-item results. Send one SCIM /Bulk request as lab-provisioning with two PATCH operations: set Ava's title to Photographer, and set Cora's active to false.

POST$ISSUER/scim/v2/Bulk Open in console
POST $ISSUER/scim/v2/Bulk HTTP/1.1
Authorization: Bearer $SCIM
Content-Type: application/scim+json

{"schemas":["urn:ietf:params:scim:api:messages:2.0:BulkRequest"],"Operations":[{"method":"PATCH","path":"/Users/<Ava's user ID>","data":{"schemas":["urn:ietf:params:scim:api:messages:2.0:PatchOp"],"Operations":[{"op":"replace","path":"title","value":"Photographer"}]}},{"method":"PATCH","path":"/Users/<Cora's user ID>","data":{"schemas":["urn:ietf:params:scim:api:messages:2.0:PatchOp"],"Operations":[{"op":"replace","path":"active","value":false}]}}]}

The response lists a result per operation: Ava's change succeeds and Cora's fails with the same protected_user error as step 6.

Why it matters: a batch needs a decision per item, never one decision for the first item or for the request as a whole, as the lesson's batch approval shows.

Check your work

Press Check my progress. The checks follow Cora's Auditor assignment, Ben's allowed edit, his refused edit of a role holder and his refused email change.

Also confirm by hand:

  • The SCIM read returned only the fields you asked for.

  • Step 6 and the second Bulk operation were refused with protected_user.

Cleanup

  • Remove Ava's title if you want her record back to the start.

  • Keep Auditor on Cora, the extended Help desk on Ben, and both SCIM clients.

Missing infrastructure

  • G3 and G22, the photo library lab API. With seeded albums 4410 and 5230 and photos 9001 and 7310, the full lab would request /albums/4410/photos/7310 as Ava in the role of Lena and get 404, not 200; move photo 9001 to album 5230 as Ben in the role of Omar and be refused at the destination; get a short-lived signed thumbnail link only after photo.view; run a search whose total and filter counts match the filtered set; and send a PATCH with uploaded_by and status to see field_not_editable.

Back to all labs

We value your privacy

We use cookies and similar technologies to enhance your browsing experience, and analytics to understand our traffic. By clicking "Allow All", you consent to optional analytics. Cookie Policy

The Lab