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

Bring a joiner in disabled, link her history, and let her activate on day one

Find Mia's earlier internship record by a stable ID, link her new hire to it, give her day-one access while the account stays disabled, enable it on the start date and let Mia set up her own sign-in.

ReadyUses your lab tenant

The lesson

Builds on: What a directory holds.

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. Link the new hire to her existing record

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

  2. Give the disabled account its team group

    Recorded as scim.group.patch succeeded for lab-provisioning.

  3. A sign-in before the start date is refused

    Recorded as account.sign_in rejected (account_locked).

  4. Enable the account on the start date

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

  5. Mia sets her own password from her mailbox

    Recorded as account.reset_password succeeded (reset_completed).

  6. Mia signs in for the first time

    Recorded as account.sign_in succeeded (signed_in).

Setup

Mia Lane is joining Lab Photos as a print technician. She is the lab's person with a real inbox, so her activation email reaches you. In 2024 she spent a summer as an intern in print support, recorded under the register ID C1874. HR now sends this hire record:

{
  "type": "hire",
  "employeeNumber": "E10482",
  "legalName": { "given": "Mia", "family": "Lane" },
  "jobTitle": "Print technician",
  "department": "Print support",
  "manager": "Cora Diaz",
  "startDate": "<a date next week>",
  "previousRegisterId": "C1874"
}
  1. Open a bash shell and set the variables and helpers from the directory lab, plus Mia's address: export [email protected] (your own plus-address).

  2. On Users, check whether a user with $MIA_EMAIL already exists from another track. If one does, delete it: this lab needs her 2024 record to be the only one.

  3. Create Mia's 2024 internship record, inactive, as the history the hire should find.

jq -n --arg ent "$ENT" --arg email "$MIA_EMAIL" '{schemas:["urn:ietf:params:scim:schemas:core:2.0:User",$ent],
  externalId:"C1874", userName:"mia.lane", name:{givenName:"Mia",familyName:"Lane"},
  emails:[{value:$email,type:"work",primary:true}], active:false, ($ent):{department:"Print support",title:"Summer intern"}}' \
  | scim -X POST "$SCIM/Users" --data-binary @- | jq '{id, externalId, active}'
export CORA=$(scim -G "$SCIM/Users" --data-urlencode 'filter=userName eq "[email protected]"' | jq -r '.Resources[0].id')
  1. Press Start on the lab page.

Walkthrough

  1. Look before creating. Search by the new employee number, then by the earlier register ID, then by name alone.

for f in 'externalId eq "E10482"' 'externalId eq "C1874"' 'name.familyName eq "Lane"'; do
  scim -G "$SCIM/Users" --data-urlencode "filter=$f" | jq -c --arg f "$f" '{filter:$f, found:.totalResults}'; done

The answers are 0, 1, and 1 or more.

Why it matters: this is "Creating the identity". The register ID is a stable identifier both records hold, strong enough to link. A name match is only a candidate for a person to check, and creating a second record would split Mia's history in two.

  1. Link the hire to the existing record. Keep active false.

export MIA=$(scim -G "$SCIM/Users" --data-urlencode 'filter=externalId eq "C1874"' | jq -r '.Resources[0].id')
jq -n --arg ent "$ENT" --arg cora "$CORA" '{schemas:["urn:ietf:params:scim:api:messages:2.0:PatchOp"],Operations:[
  {op:"replace",path:"externalId",value:"E10482"}, {op:"replace",path:"title",value:"Print technician"},
  {op:"replace",path:($ent+":employeeNumber"),value:"E10482"}, {op:"replace",path:($ent+":manager"),value:{value:$cora}}]}' \
  | scim -X PATCH "$SCIM/Users/$MIA" --data-binary @- | jq '{id, externalId, active}'

Same id, new externalId, active: false. Search Audit for $MIA: the 2024 create and today's link sit under one subject.

Why it matters: linking brings back history, not access. Nothing from the internship grants anything to the new job.

  1. Day-one access while disabled. Add Mia to Print support, the team group her job brings.

export PRINT=$(scim -G "$SCIM/Groups" --data-urlencode 'filter=displayName eq "Print support"' | jq -r '.Resources[0].id')
jq -n --arg u "$MIA" '{schemas:["urn:ietf:params:scim:api:messages:2.0:PatchOp"],Operations:[{op:"add",path:"members",value:[{value:$u}]}]}' \
  | scim -X PATCH "$SCIM/Groups/$PRINT" --data-binary @- -o /dev/null -w '%{http_code}\n'

Why it matters: this is "Access on day one". The account and its access exist a week early so slower systems can catch up, and nobody can use them yet.

  1. Prove the account is unusable. In a private window, go to $ISSUER/login and try $MIA_EMAIL with any password. Sign-in fails, and Audit (Source Protocol activity) shows account.sign_in rejected with account_locked.

Why it matters: an account that works before day one is access without a reason, and nobody would notice it being used.

  1. The start date arrives. Enable the account with a correlation ID you can search for.

export CID=$(uuidgen | tr A-Z a-z)
jq -n '{schemas:["urn:ietf:params:scim:api:messages:2.0:PatchOp"],Operations:[{op:"replace",path:"active",value:true}]}' \
  | scim -X PATCH "$SCIM/Users/$MIA" -H "X-Correlation-ID: $CID" --data-binary @- | jq '{id, active}'

Search Audit for $CID: scim.user.patch and scim.user.unlock share it.

Why it matters: the enable is a recorded change with its own trigger, not a side effect, so it can be explained later.

  1. Activation instead of a temporary password. In the private window, open $ISSUER/reset-password, enter $MIA_EMAIL, open the email and set a password. The tenant's password rules and breached-password check apply. Then sign in at $ISSUER/login and, if your authentication policy offers TOTP or a passkey, enroll one at $ISSUER/account/security. Audit records account.reset_password with reset_completed, then account.sign_in with signed_in.

Why it matters: this is "The first sign-in". Whoever completes it controls the account. Here that is whoever controls Mia's mailbox, and no password ever travelled through a manager or a ticket. Compare Accept passwords over SCIM, which lets a provisioning client set a password that someone then has to deliver.

  1. A cancelled hire. Create a second joiner who never starts, then remove the account.

jq -n '{schemas:["urn:ietf:params:scim:schemas:core:2.0:User"], externalId:"E10511", userName:"lab-tmp-eli",
  name:{givenName:"Eli",familyName:"Varga"}, emails:[{value:"[email protected]",type:"work",primary:true}], active:false}' \
  | scim -X POST "$SCIM/Users" --data-binary @- | jq -r .id

Delete it with scim -X DELETE "$SCIM/Users/<eli id>" -o /dev/null -w '%{http_code}\n' (204), then read it (404). Audit keeps scim.user.create and scim.user.delete for that ID.

Why it matters: this is "When the start date moves". A delayed start needs no change while active stays false. An account that was never enabled is removed, and the history of the cancelled hire stays.

  1. Catch a no-show. List recent joiners that are enabled.

scim -G "$SCIM/Users" --data-urlencode "filter=active eq true and meta.created gt \"$(date -u -d '7 days ago' +%Y-%m-%dT00:00:00Z)\"" \
  --data-urlencode 'attributes=userName,displayName' | jq -r '.Resources[].userName'

On Users, check each one's Last sign-in. "Never" a week after the start date is a dormant account from its first day, to report to the manager. On macOS, use date -u -v-7d +%Y-%m-%dT00:00:00Z instead.

Why it matters: the lesson's safeguard for a no-show is a report on enabled accounts nobody has used.

Break it

  1. Create Mia again with a new externalId but the same email, as a process that skipped step 1 would. The answer is 409 with scimType: uniqueness. This is the duplicate-identity mistake being refused by the tenant's Refuse the new user setting; the fix is the search in step 1, not a different address.

Check your work

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

  • scim.user.patch succeeded by lab-provisioning (step 2)

  • scim.group.patch succeeded by lab-provisioning (step 3)

  • account.sign_in rejected with account_locked (step 4)

  • scim.user.unlock succeeded by lab-provisioning (step 5)

  • account.reset_password succeeded with reset_completed (step 6)

  • account.sign_in succeeded with signed_in (step 6)

Cleanup

  1. Keep Mia as an active member of Print support. She is part of the lab cast.

  2. Confirm that lab-tmp-eli is deleted.

Missing infrastructure

  • G51: the tenant has no start date that enables an account by itself, and no one-time activation invitation that expires at the end of the start date. With them, steps 5 and 6 would not depend on you acting at the right moment, and an unused activation could not wait for someone else to find it.

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