IDENTITY FUNDAMENTALS · LAB
Least privilege for Ben, enforced on the server
Give Ben a read-only management role, send the hidden lock request directly and watch the server deny it, then grant exactly one more permission and take it away again.
Partly readyUses your lab tenant
The lesson
Builds on: Proving control of an account.
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.
- G22 End-user authorization engine (PDP, RBAC/ABAC/ReBAC for application data)
- G21 Group and custom-attribute token claims; directory roles fixed to `member`
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.
Sign in to start this lab and check your progress. Log in or create an account.
Create a role with one permission
Recorded as
tenant.roles.createsucceeded.Assign a management role to Ben
Recorded as
tenant.users.management_roles.assignsucceeded about[email protected].See Ben's direct lock request denied
Recorded as
tenant.users.lockrejected about[email protected].Lock Cora after Ben gains exactly that permission
Recorded as
tenant.users.locksucceeded about[email protected].
Setup
The tenant's own management portal is a real example of the lesson: a permission catalog, roles that group permissions, and a server that checks every request. Ben Okafor plays the help desk.
On this lab page choose Lab Photos and press Start.
On Ben's record choose Set password, store it in your password manager, and keep the issuer in the shell:
ISSUER=https://tenant-<id>.beyondthelogin.dev
Roles > Create role
Directory viewerwith only the permission to read tenant users (tenant.users.read).On Ben's record open Management roles and assign
Directory viewer. Changing Ben's roles ends any sessions he has.
Walkthrough
In a private window sign in as Ben at
$ISSUER/manage. Because Ben now holds a management role, the tenant asks for a second step. If he has none yet, register your authenticator app for him when asked. Ben sees Users, with no Create, Lock or Delete.
Why it matters: the interface hides what Ben cannot do. That is usability, not enforcement.
Write down the access decision you are about to test. Subject: Ben. Resource: Cora's record. Action: lock (
tenant.users.lock). Context: a full signed-in session with a second step.In DevTools > Application > Cookies, copy the value of
__Host-btl-oauth-sessionfor your tenant host and keep it out of your shell history. It belongs to a test user and ends when you sign Ben out in Cleanup.
read -rs BEN_SESSION
Read what Ben is allowed to read, and keep Cora's ID and version:
curl -s "$ISSUER/api/auth/tenant/users" -H "Cookie: __Host-btl-oauth-session=$BEN_SESSION" | jq '.users[] | {id, email, version}'
CORA_ID=<Cora's id>
CORA_VERSION=<Cora's version>
Send the hidden action directly:
lock() { curl -i -X POST "$ISSUER/api/auth/tenant/users/lock" -H "Origin: $ISSUER" -H "Content-Type: application/json" \
-H "Cookie: __Host-btl-oauth-session=$BEN_SESSION" \
-d "{\"id\":\"$CORA_ID\",\"version\":$CORA_VERSION,\"command_id\":\"$(node -p 'require("crypto").randomUUID()')\"}"; }
lock
Expected: 403 with "code":"access_denied".
Why it matters: the service checked the action against Ben's current role assignments on this request. Hiding the button was never the control.
In Audit find
tenant.users.lockrejected, with Ben as actor and Cora as subject.
Why it matters: a denied decision is still a decision worth recording, with its subject, resource and action.
Least privilege: Roles > Create role
Account lockerwith only the lock and unlock user permissions. Assign it to Ben as well. His session ends again, so sign in as Ben once more, copy the new cookie intoBEN_SESSIONwithread -rs, and runlock. Expected:200, and Cora is locked. Audit showstenant.users.locksucceeded with Ben as actor.
Why it matters: roles group permissions. Ben gained exactly one capability, not "administrator".
As yourself, unlock Cora and refresh
CORA_VERSIONfrom the list in step 4. RemoveAccount lockerfrom Ben, sign in as Ben again, copy the new cookie and runlock. Expected:403again.
Why it matters: the decision uses Ben's current assignments, not what he could do a few minutes ago.
Planned walkthrough
These steps need a per-tenant photo library API with its own authorization engine (G22). They show the lesson's album example exactly as it will run.
Create album 42 owned by Ava and share it read-only with Ben, then write the policy "owner may delete, invited viewer may read".
As Ava, delete a photo in album 42. Allowed:
curl -i -X DELETE "$ISSUER/lab-api/photos/albums/42/photos/7" -H "Authorization: Bearer $AVA_TOKEN"
As Ben, send the same request. Expected:
403, and a decision log entry naming subject, resource, action and the rule that applied.As Ava, change the album number to 43, an album she does not own. Expected:
403. Knowing an identifier never grants access to what it names.Add an attribute rule: downloading unpublished photos requires department
Communicationsand a managed device. Cora's department came from HR in the Establishing an identity lab; Ben has none, so only Cora's download is allowed.
Break it
Run the request from step 5 with no Cookie header. Expected:
401. Compare it with the403: 401 asks "who are you?", while 403 says "we know who you are, and the answer is no".While Ben still holds
Account lockerin step 7, runlocka second time with the oldCORA_VERSION. Expected:409. The server also refuses to act on stale information about the resource.
Check your work
Press Check my progress. It looks for:
tenant.roles.createsucceeded.tenant.users.management_roles.assignsucceeded for Ben.tenant.users.lockrejected for Cora (Ben without the permission).tenant.users.locksucceeded for Cora (Ben withAccount locker).
Cleanup
Make sure Cora is unlocked.
Remove all management roles from Ben and delete
Account locker. KeepDirectory viewerunassigned if you like.Sign Ben out at
$ISSUER/accountso the copied cookie stops working, then rununset BEN_SESSION.
Missing infrastructure
G22 End-user authorization engine. There is no policy decision point or sample photo service with albums, owners and invited viewers. Once it exists, the planned walkthrough evaluates real RBAC and attribute rules against real album requests and shows each decision.
G21 Group and custom-attribute token claims. Department and device attributes cannot reach tokens today, so an application could not receive the attributes the planned attribute rule needs.