IDENTITY SECURITY · LAB
Control what apps can ask for, review consent and withdraw a grant
Keep a sensitive scope away from lab-printer-app until you assign it, read the consent screen Ava sees, deny and approve it, then remove the scope and watch its grant end.
Partly readyUses your lab tenant
The lesson
Builds on: Limiting what stolen access can do.
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.
- G3 Sample protected resource API; no RFC 9728 protected resource metadata
- G5 Device authorization grant
- G40 Grant and consent inventory: list and revoke a user's app grants ("connected applications"), app approval policy, tenant-wide app block
- G53 Client display metadata on consent (logo, policy links)
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.
lab-printer-app cannot ask for a scope it was not given
Recorded as
oauth.authorizerejected (invalid_scope) forlab-printer-app.Assign photos.share and always ask for consent
Recorded as
tenant.oauth.clients.updatesucceeded.Ava declines the consent screen
Recorded as
oauth.authorizerejected (access_denied) forlab-printer-appabout[email protected].Ava approves and the app receives a code
Recorded as
oauth.authorizesucceeded (code_issued) forlab-printer-appabout[email protected].Remove photos.share from the app
Recorded as
tenant.oauth.clients.updatesucceeded.
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
Press Start on this page.
In OAuth > Scopes, confirm
photos.shareexists and is exclusive. Create it if the Lab Photos preset did not.You need
lab-printer-appand fresh PKCE and state values fromeval "$(btl-lab pkce)"andeval "$(btl-lab state)", withCLIENT_IDset to its client ID.
Walkthrough
Ask for
photos.sharebefore the client has it. The browser lands on the callback page witherror=invalid_scope.
GET$ISSUER/oauth/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=https%3A%2F%2Fbeyondthelogin.dev%2Flab%2Fcallback%2F&scope=photos.read%20photos.share&code_challenge=$CHALLENGE&code_challenge_method=S256&state=$STATE
Open in console
GET $ISSUER/oauth/authorize?response_type=code&client_id=$CLIENT_ID&redirect_uri=https%3A%2F%2Fbeyondthelogin.dev%2Flab%2Fcallback%2F&scope=photos.read%20photos.share&code_challenge=$CHALLENGE&code_challenge_method=S256&state=$STATEWhy it matters: an exclusive scope is granted to a client on purpose by an administrator, not by asking. An app that wants to share albums has to be reviewed first, which is the lesson's "deciding which apps people can approve."
In OAuth > Clients, assign
photos.sharetolab-printer-appand set its consent mode to always. Save. Send the same request again and sign in as Ava. Read the consent screen: it names the app and lists each scope with its description.
Why it matters: the consent screen is the only place Ava sees what she is granting. Ask the lesson's questions: did she start this, is the app the one she expected, and does printing photos need permission to share albums?
Choose to deny. The callback page shows
error=access_denied.
Why it matters: refusing is a real outcome, recorded with Ava as the subject. In the lesson's consent phishing, nothing else about the sign-in looks wrong, so this decision is the defense.
Send the request once more and approve. The callback page shows a
code. Note it is the genuine sign-in page and Ava completed it with her usual method.
Why it matters: no credential was stolen and no page was faked. A grant approved this way keeps working after a password change until it is revoked, which is why approvals need the same care as sign-ins.
Remove
photos.sharefromlab-printer-appand save. Changing what a client may request revokes the tokens and remembered consent it received under the old settings.
Why it matters: scope minimization. If the printer does not need to share albums, it should not hold that permission, and removing it ends what was already granted.
Check the device flow your tenant offers. It answers that device authorization is not implemented.
POST$ISSUER/oauth/device_authorization
Open in console
POST $ISSUER/oauth/device_authorization
Content-Type: application/x-www-form-urlencoded
client_id=$CLIENT_ID&scope=photos.readWhy it matters: the lesson's device code phishing needs a device flow to exist. Where nobody needs device sign-in, not offering it is the strongest setting.
Review grants by event. In Audit, source Protocol activity, filter
succeededand list theoauth.authorizeevents withcode_issued: which client, which user, and when.
Why it matters: approvals accumulate. Without a grant inventory, the Audit history is your only list of who approved which app.
Break it
Set
lab-printer-app's consent mode to skip and save. Run the flow as Ava: no consent screen appears, and the code arrives at once. Skipping consent suits first-party apps the organization runs itself, never a third-party app.
Restore: set the consent mode back to remember (or always) and save.
Check your work
Check my progress confirms the refused scope, the client change, the declined consent, the approval and the scope removal.
In Audit, source OAuth management, the two
tenant.oauth.clients.updateevents show who changed the client.Your notes answer the lesson's four approval questions for the screen Ava saw.
Cleanup
Confirm
lab-printer-appno longer hasphotos.shareand its consent mode is remember.Keep
photos.shareas a scope; later labs may assign it again.
Missing infrastructure
G40: there is no per-user list of app grants with scopes and last use, no per-grant revoke, no tenant-wide app block, and no rule that sends sensitive scopes to an administrator for approval. Once it exists, the lab will set
photos.shareto need administrator approval, have Ava request it, approve it as Tenant Admin, and then block the app tenant-wide.G53: the consent screen shows the client's name and scopes, but no logo, publisher or policy links, so there is no publisher to verify. Once it exists, the lab will add display metadata and compare the screens.
G5: device authorization is not implemented, so device approval screens cannot be reviewed. Once it exists, the lab will start a device request for
lab-printer-appand read how the approval screen names the device.G3: there is no sample photo API that enforces
photos.share, so the effect of withdrawing the scope is seen at the tenant, not at an API. Once it exists, the lab will call the share endpoint before and after removal.