Directory sources

Proofroom keeps its own copy of who works at your organisation (People). That copy comes from exactly one directory source, chosen by an admin on Admin → People:

Source Use it when How people arrive
SCIM Your identity provider (Microsoft Entra ID, Okta, JumpCloud, Authentik, …) can provision to a SCIM 2.0 app. The provider pushes changes; Proofroom syncs about 10 seconds later, and nightly.
CSV upload You have an export from an HR system or a spreadsheet. An admin uploads a file, checks a preview and confirms.
Microsoft Graph You use Microsoft Entra ID and prefer Proofroom to pull. A nightly read of the tenant (and Sync now).

Everything here is done by admins; the screens need the people.manage power.

Choosing a source

A new install has no source until an admin picks one on Admin → People, and the nightly directory sync skips until then. (Installs that already had people when this feature arrived were set to Microsoft Graph by the migration, so nothing changes for them.)

  • One source at a time. Choose it with the radio buttons (“Not configured”, “Microsoft Graph”, “SCIM”, “CSV upload”), then Change source and confirm.
  • Every person is keyed by their source and an id from that source. When you switch, people are adopted by email: someone already in People whose email matches exactly one person from the new source keeps their history (roles, exclusions, change log) and moves to the new source. Adoption needs the same address in both sources: a person whose email differs between the old and the new source joins as a new person, and the old record is from the previous source (see below). Check the mapping (for SCIM, the attribute that feeds emails) before switching.
  • People from the old source who find no match are “from the previous source”. On the first Graph run (after two consecutive misses, as always) or the first CSV import they are marked as left. Under SCIM they are never marked as left automatically: the provider may not have pushed everyone yet. A card, “People from the previous source”, on Admin → People shows how many are not yet matched; once your provider has pushed everyone, press Mark as left. That is recorded as a leaver but does not count as a fresh read of the directory (see “Freshness”).
  • Switching away from SCIM revokes its token and deletes the users and groups the provider pushed. Switching away from CSV discards a pending preview.
  • Returning to SCIM after another source starts from an empty store: generate a new token and have the provider push everyone again (Entra ID: the application’s Provisioning, Restart provisioning; Okta: Push Users or re-assign the app; others: a full sync from the provider). The re-push gives everyone new SCIM ids, so until it completes, both the people from the other source and the people SCIM pushed before you left are “from the previous source”: they are matched by email as the provider pushes them again (keeping their history), and are not marked as left. Anyone still unmatched once the re-push is done is counted on the “People from the previous source” card.
  • Audit events: people.source_changed, people.previous_source_retired.

SCIM

Endpoint: https://<your host>/scim/v2 (the host is AUTH_URL; Admin → People shows the exact URL with a copy button). It answers 404 unless SCIM is the chosen source, and 401 unless the bearer token matches.

Token. On Admin → People, with SCIM chosen, press Generate token. The token (it starts scim_) is shown once: copy it into your identity provider then. Only its SHA-256 hash is stored, and neither the token nor request bodies are logged. Replace token invalidates the old one at once; Revoke token turns the endpoint off. There is one token. The card shows when it was created and the last request (time and address). Audit events: people.scim_token_created, people.scim_token_revoked.

What is supported. Users (create, read, list, replace, patch, delete) and Groups, the discovery endpoints (ServiceProviderConfig, Schemas, ResourceTypes), PatchOp in the shapes Entra ID and Okta send, and filters of the form userName eq "..." or externalId eq "..." (Users) and displayName eq / externalId eq (Groups). Not supported: bulk, ETags, sorting, other filters. Requests over 1 MB get 413. Lists return 100 users by default, at most 200 per page.

Attribute mapping

SCIM attribute Proofroom
emails: the primary one, else the work one, else the first; userName only when there is none and it is an email address Email (how people are adopted from another source and linked to app accounts)
displayName, or name (formatted, or given and family) Name
title Job title
userType Employment type
urn:ietf:params:scim:schemas:extension:enterprise:2.0:User department Department
Enterprise extension manager Manager: its value is matched to a pushed user by Proofroom’s id, else by the user’s externalId (the provider’s own id, as Okta sends), else by userName (any case)
id (the one Proofroom issues on create) The person’s key within the source. The provider’s own externalId is stored and used only to resolve managers and answer externalId eq filters
active Status

Deprovisioning. active: false marks the person disabled (their history is kept). A DELETE marks them left at once; there is no two-miss rule under SCIM. A password sent by Okta is discarded, never stored. SCIM groups are stored but not used: they do not feed Admin → Groups or roles.

When it syncs. A push only records the change; a sync applies it about 10 seconds later (further pushes in that window share the one sync), and a sync also runs nightly. The sync history on Admin → People shows each run.

Microsoft Entra ID

  1. Entra admin center, Enterprise applications, New application, Create your own application, “Integrate any other application you don’t find in the gallery (Non-gallery)”. Name it Proofroom.
  2. Open the application, Provisioning, Get started, set Provisioning Mode to Automatic.
  3. Tenant URL: https://<your host>/scim/v2. Secret token: the token from Admin → People. Test Connection, then Save.
  4. Mappings, Provision Microsoft Entra ID Users: keep the defaults userPrincipalName to userName and mail to emails[type eq "work"].value (Proofroom takes the email from emails, so it is the mail address, the one Microsoft Graph uses, even where the UPN differs), displayName, jobTitle to title, department and manager to the enterprise extension, accountEnabled to active. Remove mappings you do not want to share, but keep mail: a user pushed without it falls back to the UPN. Leave Groups provisioning on or off; Proofroom does not use it.
  5. Assign the users or groups to push under Users and groups. Use Provision on demand on one user to test, then set Provisioning Status to On.

Entra sends a first push of everyone, then incremental changes about every 40 minutes. A user removed from the assignment is deactivated (active: false) or deleted, depending on your Entra settings.

Sign-in with Entra ID. App accounts are linked to People by email. Add the optional email claim to the sign-in app registration (Token configuration, Add optional claim, ID token, email; see the sign-in settings) so an account carries the user’s mail address, the same one SCIM and Graph give People. Without it the account’s email can be the UPN, and where the UPN differs from mail the account is not linked to its person.

Okta

  1. Admin console, Applications, Browse App Catalog, SCIM 2.0 Test App (Header Auth) (or any SCIM 2.0 header-auth app). Under Provisioning choose Integration, Configure API Integration.
  2. SCIM connector base URL: https://<your host>/scim/v2. Unique identifier field for users: userName. Leave the import actions off (Proofroom is push only) and tick Push New Users and Push Profile Updates.
  3. Authentication Mode: HTTP Header, Authorization = Bearer <token>. Test Connector Configuration, Save.
  4. To App settings: enable Create Users, Update User Attributes and Deactivate Users. Map title, department, manager and userType in the attribute mappings, then assign people.

JumpCloud

  1. Admin portal, User Authentication (or SSO Applications), Get Started, a Custom SCIM Connector application. Name it Proofroom.
  2. Identity Management, SCIM version 2.0. Base URL: https://<your host>/scim/v2. Token Key: the Proofroom token. Test Connection, then Activate.
  3. Assign user groups and let JumpCloud push. Disabled or removed users are deactivated or deleted by JumpCloud’s own setting.

Authentik

  1. Admin interface, Applications, Providers, Create, SCIM Provider. URL: https://<your host>/scim/v2. Token: the Proofroom token.
  2. Keep the default user property mapping, or add one that sets title, department and userType. Optionally limit by group.
  3. Attach the provider to an application as a Backchannel provider and run a sync from the provider page.

CSV

Upload on Admin → People with CSV upload chosen. Download template gives a file with the right header; Export people (with leavers) gives the directory in the same format, the current source’s leavers included with status left and their end date, except one whose email or id a current person holds (1.14.2; earlier exports left leavers out).

The file is UTF-8 (a byte-order mark is stripped), comma separated, RFC 4180 quoting, at most 5 MB and 20,000 data rows. Example:

email,name,title,department,manager_email
lead@example.test,Lee Lead,Head of Engineering,Engineering,
eng@example.test,Eli Engineer,Engineer,Engineering,lead@example.test

Columns

Column Required Meaning
email yes Unique per row, case-insensitive
name yes Display name
employee_id no Stable id from your HR system
title no Job title
department no Department
employment_type no For example Employee, Contractor
manager_employee_id no The manager’s employee_id
manager_email no The manager’s email
status no active (default), disabled, or left (also leaver, terminated)
start_date no First day of employment, YYYY-MM-DD
end_date no Last day of employment, YYYY-MM-DD. A row with an end date, or with status left, is a leaver

Header names ignore case, spaces, hyphens and underscores, and these aliases are understood: email address, display name and full name (name), job title, employee type (employment type), and similar spellings of the rest. Other columns are ignored and listed in the preview. A missing email or name column is reported as one header error, and a file with a header but no people is refused (with absence meaning left, it would mark everyone as left).

Keys. Each person’s id is employee_id when that column has values, otherwise their lowercased email. A column that is entirely empty counts as absent; one that is partly empty is an error (rows are named). Using employee_id lets a person’s email change without becoming a different person.

Managers are found by manager_email, or by manager_employee_id when the employee_id column has values (manager_employee_id is ignored when people are keyed by email: use manager_email then). With employee_id keys, manager_employee_id wins when both are given. A manager who is not in the file or the directory is kept as an unresolved reference, and the person’s page shows the manager as “Not in the directory”.

Errors. Errors name the row (the header is row 1) and the first 50 are shown, counted as records. Fix the file and upload again; nothing is imported while any error remains.

Preview and confirm. Upload and preview shows what confirming would do, from the same comparison the other sources use: “N join · N leave · N change · N unchanged · N matched from the previous source”, with each person listed. Confirm import applies it; Discard drops it. The preview expires after 1 hour, and is refused if the directory changed since it was made (for example by another import): upload again. With CSV, people missing from the file are marked left at once, on the day of the upload. To record the real day someone left (which offboarding evidence counts from), keep them in the file with status left and their end_date; a later file can correct the day of someone already marked left. An end date in the future is refused: upload them as active, and again once they have left. A leaver row for someone Proofroom never had is kept as a record of who left and when. Audit events: people.csv_imported, people.csv_discarded.

Round trip. An export cell that starts with =, +, - or @ is prefixed with ' so spreadsheets do not run it as a formula; the import strips that prefix. Exporting and re-importing the unchanged file is therefore a no-op.

Microsoft Graph

Proofroom reads the tenant’s users nightly at 03:00 UTC, and on Sync now. It needs credentials in one of two forms:

  • GRAPH_TENANT_ID, GRAPH_CLIENT_ID and GRAPH_CLIENT_SECRET in .env (all three or none), for a dedicated app registration. Use this when sign-in is not Entra ID, or you want a separate app.
  • Otherwise the Entra sign-in app registration (OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET; sign-in settings).

The app registration needs the Microsoft Graph application permission User.Read.All, with admin consent granted. Admin → People shows which settings are in use, Test directory access checks the permission, and Sync now runs a sync.

People who disappear from the tenant are marked left only after two consecutive syncs miss them. If two accounts in the tenant share an email, the run fails and the error names the duplicate; fix it in Entra and sync again.

Freshness

Evidence collection refuses directory data that is too old. The limit is Freshness (days) on Admin → People (1 to 366, default 7); the People directory connector’s collectors fail without filing anything when the newest successful read of the directory is older. Admin → People shows the same warning. Reads that count: a successful sync, SCIM sync or confirmed CSV import. Mark as left does not count, since nothing was read from your directory.