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 AND TRUST · LAB

Resolve a patient to one record and record what proofing established

Load a clinic's patient records into your tenant, resolve an applicant with the fewest attributes that work, record the proofing outcome without the evidence, and see why credential reset needs equal protection.

Partly readyIncludes a simulationUses your lab tenant

The lesson

Builds on: Establishing an identity.

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.

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. Define where the clinic's patient attributes live

    Recorded as tenant.schemas.create succeeded.

  2. Load the patient records in one Bulk request

    Recorded as scim.bulk succeeded.

  3. Resolve the applicant with a SCIM filter

    Recorded as scim.users.search succeeded.

  4. Record the proofing outcome on the patient

    Recorded as scim.user.patch succeeded.

  5. Reset a user's sign-in methods as an administrator

    Recorded as tenant.users.methods.reset succeeded about [email protected].

Setup

For this lab and the next, Lab Photos plays the lesson's clinic. The patient records and their schema are temporary and carry the lab-tmp- prefix.

  1. On this lab page choose Lab Photos and press Start.

  2. Schemas: create a custom extension schema named lab-tmp-clinic-patient with the string attributes dateOfBirth, postalCode, patientNumber, proofingLevel, proofingMethod and proofedAt. Copy the schema URN it shows.

  3. Get a provisioning token as in the Establishing an identity lab, and keep the schema URN:

ISSUER=https://tenant-<id>.beyondthelogin.dev
SCIM="$ISSUER/scim/v2"
EXT='<schema URN from the Schemas page>'
PROV_ID=<lab-provisioning client ID>
read -rs PROV_SECRET
SCIM_SCOPE=<the SCIM scope that can write users>
SCIM_TOKEN=$(curl -s -u "$PROV_ID:$PROV_SECRET" -d grant_type=client_credentials -d scope="$SCIM_SCOPE" "$ISSUER/oauth/token" | jq -r .access_token)

Walkthrough

  1. Load three patients in one SCIM Bulk request: two named Alex Rivera, born on the same day and living at different postal codes, and an Alexandra Rivera.

p() { echo "{\"method\":\"POST\",\"path\":\"/Users\",\"bulkId\":\"$1\",\"data\":{\"schemas\":[\"urn:ietf:params:scim:schemas:core:2.0:User\",\"$EXT\"],\"userName\":\"[email protected]\",\"emails\":[{\"value\":\"[email protected]\",\"primary\":true}],\"name\":{\"givenName\":\"$2\",\"familyName\":\"Rivera\"},\"$EXT\":{\"dateOfBirth\":\"$3\",\"postalCode\":\"$4\",\"patientNumber\":\"$5\"}}}"; }
curl -s -X POST "$SCIM/Bulk" -H "Authorization: Bearer $SCIM_TOKEN" -H "Content-Type: application/scim+json" \
  -d "{\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:BulkRequest\"],\"Operations\":[$(p alex1 Alex 1990-04-12 02139 P-48213),$(p alex2 Alex 1990-04-12 94110 P-51877),$(p alexandra Alexandra 1988-11-02 02139 P-30551)]}" | jq '.Operations[] | {bulkId, status}'

Expected: three operations with status 201.

  1. Which person? Resolve by name only:

search() { curl -s -G "$SCIM/Users" -H "Authorization: Bearer $SCIM_TOKEN" --data-urlencode "filter=$1" --data-urlencode "attributes=id" | jq '{totalResults, ids: [.Resources[].id]}'; }
search 'name.givenName eq "Alex" and name.familyName eq "Rivera"'

Expected: "totalResults": 2.

Why it matters: two records match equally well. The portal must stop here rather than pick one and continue as though it had found Alex.

  1. Add the date of birth, then the postal code:

search "name.givenName eq \"Alex\" and name.familyName eq \"Rivera\" and $EXT:dateOfBirth eq \"1990-04-12\""
search "name.givenName eq \"Alex\" and name.familyName eq \"Rivera\" and $EXT:dateOfBirth eq \"1990-04-12\" and $EXT:postalCode eq \"02139\""
ALEX_ID=<the single id from the last result>

Expected: still 2 with the date of birth, then exactly 1.

Why it matters: resolution needs enough attributes to be unambiguous in this population, and no more. No employer or national identification number was needed.

  1. Look at attributes=id in the helper: every response carries only the ID.

Why it matters: each extra attribute returned is something else to store, protect and eventually delete.

  1. Give one general answer. In a private window, submit [email protected] at $ISSUER/reset-password, then [email protected]. Both show the same page with the same status. Audit records what actually happened for each.

Why it matters: this is the lesson's "No patient with that date of birth" problem. A single general response stops the form from confirming which details are right, while the service still knows.

  1. Is the evidence genuine, and does it belong to Alex?

Simulation. the tenant has no document, face or liveness checks (G34), so this decision is made on paper. For the resolved Alex, assume a license whose surname differs from the record after a life change, a selfie too dark to compare, and a clinician who has treated Alex for years. Choose the alternate route (staff review with the clinician as a reference, or an in-person visit) and the confidence level a lab-results portal needs, and write down why.

  1. After proofing succeeds, record the outcome, not the evidence:

curl -s -X PATCH "$SCIM/Users/$ALEX_ID" -H "Authorization: Bearer $SCIM_TOKEN" -H "Content-Type: application/scim+json" \
  -d "{\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],\"Operations\":[{\"op\":\"add\",\"path\":\"$EXT:proofingLevel\",\"value\":\"IAL2\"},{\"op\":\"add\",\"path\":\"$EXT:proofingMethod\",\"value\":\"staff review with clinician reference\"},{\"op\":\"add\",\"path\":\"$EXT:proofedAt\",\"value\":\"$(date -u +%FT%TZ)\"}]}" | jq ".\"$EXT\""

Why it matters: the record says what was checked, how and when, and that an alternate route was used. There is deliberately no attribute for a license image or a face template.

  1. Proofing describes a moment; credentials carry it forward. On Ava's record choose Reset sign-in methods. Her authenticator app and passkey are gone, and Audit shows tenant.users.methods.reset with you as actor. In Roles, note that this action has its own permission, separate from reading users.

Why it matters: whoever can replace credentials can take over a proofed account. That action must be at least as protected as the proofing, and recovery may need to repeat part of it.

Planned walkthrough

These steps need a proofing service (G34) and proofing claims in tokens (G21).

  1. Proofing > configure which routes satisfy which assurance level, and require validation and verification for the lab-results level.

  2. An applicant submits sample document images and a short selfie video. The service reports validation (security features, expiry, issuer record match) and verification (face similarity, liveness) as separate results.

  3. An inconclusive result goes to a staff review queue instead of failing. A successful one writes a proofing record and links the account to the resolved patient.

  4. Map proofingLevel into an ID token claim, so an application can require "proofed to IAL2" from the token itself.

Break it

  1. Run search 'name.familyName eq "Rivera"' and imagine code that takes .Resources[0]. It would link the portal account to whichever Rivera the server happened to return first.

  2. Filter on an attribute that does not exist: search "$EXT:birthday eq \"x\"". Expected: 400 with "scimType":"invalidFilter".

Check your work

Press Check my progress. It looks for:

  • tenant.schemas.create succeeded (Audit also shows tenant.schemas.attributes.create).

  • scim.bulk succeeded.

  • scim.users.search succeeded.

  • scim.user.patch succeeded for Alex.

  • tenant.users.methods.reset succeeded for Ava.

Audit also shows account.reset_password for both addresses in step 5.

Cleanup

  1. As Ava, sign in with her password and register her authenticator app and passkey again at $ISSUER/account/security. Later labs rely on both.

  2. Keep the three patients and the lab-tmp-clinic-patient schema for the Proofing methods lab, which deletes them.

Missing infrastructure

  • G34 Identity proofing. There is no document capture, evidence validation, face comparison or liveness, and no staff review queue. Once it exists, the planned walkthrough replaces the paper decision in step 6.

  • G21 Custom-attribute claims. proofingLevel cannot be mapped into a token claim, so an application cannot require a proofing level from the token.

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