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

Reconcile every account in your tenant and act on what you find

Classify your tenant's accounts as matched, orphaned, dormant, shared or service, check token activity before calling anything unused, and act in the safe order of investigate, disable, notify, wait and delete.

ReadyUses your lab tenant

The lesson

Builds on: Sources of truth.

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. Disable the orphan before deleting anything

    Recorded as tenant.users.lock succeeded.

  2. Disable the service client nobody owns

    Recorded as tenant.oauth.clients.update succeeded for lab-tmp-labfeed.

  3. The disabled client can no longer get tokens

    Recorded as oauth.token rejected (client_disabled) for lab-tmp-labfeed.

  4. Disabling a client something depends on stops it

    Recorded as oauth.token rejected (client_disabled) for lab-hr-feed.

Setup

The lesson's pharmacy reconciliation found a hand-made account, a shared login and a forgotten service account. This setup plants the same three in your tenant, next to the simulated workforce and the people you already have.

  1. Open a bash shell and set the variables and helpers from the directory lab, plus HR_ID and HR_SECRET (read -rs).

  2. In the portal, create [email protected] (J Smith) and add it to Print support. Do not sign in as it.

  3. Create [email protected] (Front Desk) with a password you imagine Ava and Cora sharing. In a private window, sign in as it once at $ISSUER/login.

  4. In OAuth > Clients, create a confidential client lab-tmp-labfeed with client_credentials and scim-<id6>.read, and use it once, as an old integration did:

export FEED_ID=<lab-tmp-labfeed client_id>
read -rs FEED_SECRET; export FEED_SECRET
curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $(token_for "$FEED_ID" "$FEED_SECRET" "$SCIM_SCOPE.read")" "$SCIM/Users?count=1"
  1. Press Start on the lab page.

Walkthrough

  1. Matched versus unclaimed. Accounts carrying a source's key are matched; active accounts without one are unclaimed.

scim -G "$SCIM/Users" --data-urlencode 'filter=active eq true and externalId pr' --data-urlencode count=200 | jq .totalResults
scim -G "$SCIM/Users" --data-urlencode 'filter=active eq true and not (externalId pr)' --data-urlencode count=200 \
  --data-urlencode 'attributes=userName' | jq -r '.Resources[] | [.id, .userName] | @tsv'

For each unclaimed account, search Audit for its ID and note how it was created: tenant.users.create in the portal, account.register by the person, or SCIM without an externalId.

Why it matters: this is "Finding orphans". Reconciliation starts from the accounts that exist and asks whose each one is. The unclaimed list is a list of questions, not of deletions: Ava, Ben and Cora are on it too, because they were made by hand.

  1. Missed leavers. In the HR Simulator, open a lifecycle run's details and list the people it expected to lock or delete. Read each by externalId. Any that is still active is a leaver the tenant still holds; any that is locked but still in groups is a leaver whose access was disabled but not removed.

Why it matters: a missed leaver is the most common orphan, like lwong in the lesson. It shows that a removal never reached this system.

  1. Dormant accounts. On Users, scan the Last sign-in column. Choose thresholds: 45 days for anyone with a management role, 90 for everyone else. lab-tmp-jsmith2 shows "Never". Note that meta.lastModified is not usage: an HR update changes it without anyone signing in.

Why it matters: this is "Accounts nobody uses". Access nobody uses is probably access nobody needs, and nobody would notice someone else using it.

  1. Service accounts. For each client on OAuth > Clients, find its last token request: open Audit, Source Protocol activity, and search the client ID. lab-tmp-labfeed has one oauth.token from setup and no recorded owner. lab-hr-feed has many. Neither has ever "signed in".

Why it matters: this is "Shared and service accounts". A dormancy rule based on sign-ins would flag lab-hr-feed, which the HR feed depends on. Check token activity before disabling anything as unused.

  1. Shared accounts. Search Audit, Protocol activity, for Front Desk's ID. Its sign-in says Front Desk, never which person. In your notes, give it an owner and a plan: individual accounts for Ava and Cora, then lock it.

Why it matters: a shared login attributes every action to nobody in particular, and one person's access cannot be removed without changing it for everyone.

  1. Act in order. Work through each finding and record every decision, including keeps, in reconciliation.json.

    • lab-tmp-jsmith2: matches nobody, no API activity, made by hand. Lock it now and investigate after, because locking is easy to undo.

    • lab-tmp-labfeed: no owner, no current purpose. Disable it on OAuth > Clients, then confirm that a token request now fails.

    • Front Desk: lock it once Ava and Cora use their own accounts.

curl -s -u "$FEED_ID:$FEED_SECRET" -d grant_type=client_credentials -d "scope=$SCIM_SCOPE.read" "$ISSUER/oauth/token" | jq .

The token request answers invalid_client, and Audit records oauth.token rejected with client_disabled. Deletion waits until the investigation is finished.

Why it matters: this is "What to do with what you find". Investigate, disable, notify, wait, then delete. Deleting first turns a cleanup into an outage and destroys history an investigation might need.

  1. Fix the cause. lab-tmp-jsmith2 exists because accounts can be created in the portal without a source key. Write the rule you would enforce, "every new account carries an externalId or a register ID", and keep step 1's unclaimed query as its monthly check.

Break it

  1. Disable a client that something depends on. Start a short HR Simulator Onboarding wave (2 people, 5 minutes), then disable lab-hr-feed on OAuth > Clients while it runs. The run's next event cannot get a token, and the simulator stops the run with the reason token_denied. Audit records oauth.token rejected with client_disabled for lab-hr-feed.

Restore: enable lab-hr-feed again and reconnect it in the HR Simulator.

Why it matters: last sign-in said nothing about this account; its token activity did.

Check your work

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

  • tenant.users.lock succeeded (step 6)

  • tenant.oauth.clients.update succeeded for lab-tmp-labfeed (step 6)

  • oauth.token rejected with client_disabled for lab-tmp-labfeed (step 6)

  • oauth.token rejected with client_disabled for lab-hr-feed (Break it)

After Cleanup, step 1's unclaimed query returns only accounts you deliberately kept, each with a reason in reconciliation.json.

Cleanup

  1. Delete lab-tmp-jsmith2, lab-tmp-frontdesk and the lab-tmp-labfeed client, and unset FEED_SECRET.

  2. Confirm lab-hr-feed is enabled and connected.

  3. Keep reconciliation.json for the evidence lab.

Missing infrastructure

  • G52: last sign-in cannot be filtered or exported, clients have no owner or last-use field, and shared accounts have no owner field. With them, steps 3 to 5 would be queries instead of reading pages one by one.

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