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

IDENTITY GOVERNANCE · LAB

Read your tenant's directory the way an application does

Turn on SCIM, read Ava's entry and the groups with a client credentials token, and watch her ID and sign-in subject survive a rename while a cached copy goes stale.

ReadyUses your lab tenant

The lesson

Builds on: Why access needs governance.

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

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. Ask the directory what it supports, as lab-provisioning

    Recorded as scim.discovery.read succeeded for lab-provisioning.

  2. Read Ava's entry by its ID

    Recorded as scim.user.read succeeded for lab-provisioning.

  3. Rename Ava in the portal

    Recorded as tenant.users.update succeeded.

  4. Try to change membership from the user side

    Recorded as scim.user.patch rejected (mutability) for lab-provisioning.

  5. Try to put a group inside a group

    Recorded as scim.group.create rejected (invalid_value) for lab-provisioning.

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

From here on, the tenant's SCIM API is how your scripts read and change the directory, the way an application or a provisioning service would.

  1. In OAuth > Flow policy, allow the client_credentials grant for the tenant.

  2. In User Management > Provisioning, turn on Turn on the SCIM API for this tenant. Leave Accept passwords over SCIM off and When a new user's email matches an existing user on Refuse the new user. Save, then note the SCIM base URL and the full scope name scim-<id6>.

  3. In OAuth > Clients, create a confidential client named lab-provisioning with the Machine-to-machine preset, allow client_credentials, and assign the scope scim-<id6>. Copy the secret once. Later governance labs reuse this client.

  4. Give Ava a password with Set password on Users, and keep it in your password manager.

  5. In a bash shell, set the variables and two small helpers. Later governance labs repeat this block.

export ISSUER=https://tenant-<id>.beyondthelogin.dev   # from the tenant Overview
export SCIM="$ISSUER/scim/v2" SCIM_SCOPE=scim-<id6>
export ENT=urn:ietf:params:scim:schemas:extension:enterprise:2.0:User
export CLIENT_ID=<lab-provisioning client_id>
read -rs CLIENT_SECRET; export CLIENT_SECRET
token_for() { curl -s -u "$1:$2" -d grant_type=client_credentials -d "scope=${3:-$SCIM_SCOPE}" "$ISSUER/oauth/token" | jq -r .access_token; }
export TOKEN=$(token_for "$CLIENT_ID" "$CLIENT_SECRET")
scim() { curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/scim+json" "$@"; }

token_for <client id> <secret> [scopes] asks for a client credentials token, by default with the full SCIM scope. Run the TOKEN= line again whenever a request returns 401.

  1. Press Start on the lab page.

Walkthrough

  1. Confirm the directory answers. Send this request, or run scim "$SCIM/ServiceProviderConfig" | jq '{patch,bulk,filter,etag}'.

GET$ISSUER/scim/v2/ServiceProviderConfig Open in console
GET $ISSUER/scim/v2/ServiceProviderConfig HTTP/1.1
Authorization: Bearer $TOKEN
Accept: application/scim+json

The answer says patch, bulk, filter (maxResults: 200) and etag are supported.

Why it matters: this is "A shared record". Applications reach a directory through a protocol, and this is the one your tenant speaks.

  1. Note Ava's sign-in subject. Open $ISSUER/token-decoder, run the sign-in with scope openid profile email, sign in as Ava and note the ID token's sub.

  2. Read Ava's entry.

scim -G "$SCIM/Users" --data-urlencode 'filter=userName eq "[email protected]"' | jq '.Resources[0]'
export AVA=$(scim -G "$SCIM/Users" --data-urlencode 'filter=userName eq "[email protected]"' | jq -r '.Resources[0].id')
export E1=$(scim -i "$SCIM/Users/$AVA" | awk 'tolower($1)=="etag:" {print $2}' | tr -d '\r'); echo "$E1"

The entry holds id (a UUID equal to the sub from step 2), userName, name, emails, active: true, a read-only groups list with display names, and meta with created, lastModified and version. The ETag header, now in E1, matches meta.version.

Why it matters: this is "Entries and attributes". The entry is a set of named attributes, some single-valued and some lists, and only id is guaranteed never to change.

  1. Ask whether a cached copy is current. scim -o /dev/null -w '%{http_code}\n' -H "If-None-Match: $E1" "$SCIM/Users/$AVA" prints 304.

Why it matters: an application holding a copy can check freshness cheaply instead of trusting yesterday's data.

  1. Ava changes surname. In the portal, edit Ava: last name Brooks, email [email protected]. Repeat step 4 with the old E1: now 200. Read Ava again: new ETag, new userName and email, same id. Changing the email also ended Ava's sessions and revoked her tokens, so the Token Decoder's UserInfo call now fails.

Why it matters: this is "Identifiers that do not change". Three attributes changed and the identifier did not. An application keyed on email would now create an empty profile for Ava.

  1. Sign in as Ava again in the Token Decoder with [email protected]. The sub is the same as in step 2.

Why it matters: a relying party that keys on sub follows the person through the rename, as patient records did for Dana in the lesson.

  1. Groups are lists of members. Read the group, then try to change membership from the user side.

scim -G "$SCIM/Groups" --data-urlencode 'filter=displayName eq "Print support"' | jq '.Resources[0] | {id, members}'
export MOD=$(scim -G "$SCIM/Groups" --data-urlencode 'filter=displayName eq "Photo moderation"' | jq -r '.Resources[0].id')
jq -n --arg g "$MOD" '{schemas:["urn:ietf:params:scim:api:messages:2.0:PatchOp"],Operations:[{op:"add",path:"groups",value:[{value:$g}]}]}' \
  | scim -X PATCH "$SCIM/Users/$AVA" --data-binary @- | jq .

The group lists value (user IDs) and display. The PATCH returns 400 with scimType: mutability: groups is read-only, so membership changes through the Groups endpoint.

Why it matters: this is "Groups". Membership is stored on the group, and the user's groups attribute is a reflection of it.

  1. Nesting is refused. Try to create a group that contains Photo moderation.

jq -n --arg g "$MOD" '{schemas:["urn:ietf:params:scim:schemas:core:2.0:Group"],displayName:"lab-tmp-all-staff",members:[{value:$g,type:"Group"}]}' \
  | scim -X POST "$SCIM/Groups" --data-binary @- | jq .

The answer is 400 invalidValue: groups cannot contain other groups.

Why it matters: this directory avoids the lesson's "Students on placement inside All nursing staff" problem entirely. Many directories allow nesting, and their applications disagree about expanding it.

  1. How an application checks membership. In the Token Decoder, look at Ava's ID token and UserInfo: there is no groups claim. An application that needs to know whether Ava is in Print support must ask the directory with its own credential, as in step 7, or keep a copy that can go stale, as in steps 4 and 5.

Why it matters: this is "How applications use a directory". Every copy and cache is a place where a change has not arrived yet.

Break it

  1. Call the API with no token: curl -s -o /dev/null -w '%{http_code}\n' "$SCIM/Users" prints 401, and the WWW-Authenticate header says Bearer realm="SCIM".

  2. Read an ID that does not exist: scim "$SCIM/Users/00000000-0000-4000-8000-000000000000" returns 404 with "User not found."

Check your work

Press Check my progress. The checks look for, in order:

  • scim.discovery.read succeeded for lab-provisioning (step 1)

  • scim.user.read succeeded for lab-provisioning (step 3)

  • tenant.users.update succeeded (step 5)

  • scim.user.patch rejected with mutability (step 7)

  • scim.group.create rejected with invalid_value (step 8)

Also compare your notes: the same sub in both Token Decoder sign-ins, and different ETags before and after the rename.

Cleanup

  1. Edit Ava back to last name Archer and email [email protected].

  2. Keep the SCIM API on, lab-provisioning, and your shell block. Every later governance lab uses them.

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