ACME HQDemo work items and pages, made up for Emargen's demo. Not a real company.
Engineering / Engineering / Architecture overview

identity-service

By Marek Nowak · created 9 Mar 2020, 08:30 UTC · last updated 22 Oct 2026, 15:10 UTC · version 31

identity-serviceapprovalssamlrolesarchitecture

Repository: Emargen/acme-identity-service · Owner: Marek Nowak (approvals, SAML), with Elif Yildiz and Jonas Berg · Stack: PHP 8.4, Laravel 13, PostgreSQL 16 (RDS, one per region), Redis (cache and rate limits); sign-in tokens are rows in PostgreSQL (Sanctum)

identity-service signs people in, keeps who they are, where they work and what roles they hold, and decides who approves each access request. Everything else reads from it.

Code map

FolderWhat lives there
src/SamlDepotSamlSettings (each depot's identity provider: entity ID, sign-in URL, certificate), DepotSamlConfig (each depot's own sign-in address on our side), EnableDepotSaml (the per-depot switch), SamlSignIn, AssignRolesFromClaims, GroupRoleMap, MetadataImport (metadata upload)
src/PoliciesSignInPolicy: second factor, session length and administrators' addresses, per client
src/RolesRoleAssignment: who holds which role, where it came from (portal or saml), and since ACM-247 the depot it covers
src/Requestsaccess requests and their states, SubmitAccessRequest (the required reason), RequestMove
src/Accessaccess items, grants, request bundles, the Access report
src/ApprovalsApprovalRouting, ApprovalPolicy, deciding, moving waiting requests when a role changes
src/ClientsClient and its settings, Region, CurrentClient
src/OnboardingStartOnboarding, FinishOnboarding, ChangeRegion
src/Jobsscheduled jobs (below)
tools/the local SAML test IdP, see Local development

People, depots and roles

Each person has one home depot (people.home_depot_id, never empty) and may work at others (depot_assignments, one row per depot with the role there: driver, dispatcher or depot manager). An agency driver without a depot of their own is given the depot of their first shift. Assignments have no end date. Jonas's Claude Code wrote this up in ACM-309.

Client-wide roles are Administrator, Approver and Member. Since 1 October a role assignment can carry a depot (role_assignments.depot_id); empty means every depot, as before, so nothing changed for clients who don't set one (ACM-247, #412 and #431).

How a request finds its approver

// src/Approvals/ApprovalRouting.php:47
return $request->person->homeDepot->approvers();

Since 22 October (ACM-320): when a role assignment changes, the requests waiting at that depot move to whoever approves there now, or to the client's administrators with the line "No approver at this depot". Each moved request keeps who it waited on and when it moved (access_requests.moved_from_person_id, moved_at), and notifier sends the new approver one email listing what moved. A change made in the portal applies at once; one made in Entra ID applies at the person's next sign-in.

Single sign-on

Passwords, phone codes and sessions

Scheduled jobs

JobWhenWhat
DeliverOutboxEventsevery minuteposts waiting outbox_events to the notifier, signed
SendApproverRemindershourlyreminders at 24 hours, then daily
ExpireRequests02:00closes requests older than 14 days
PruneSignInLog03:00drops sign-in log rows older than 90 days
PruneApprovalAudit03:00drops approval decisions older than 400 days
BackUpClients01:30backs up each client to its own region's storage
CheckSamlCertificates06:00warns about certificates that expire soon, see SAML certificates

Change history

VersionDateWhoWhat
2611 Jun 2026MarekPhone codes, MFA for administrators
2714 Sep 2026MarekLockout and rate limits
282 Oct 2026MarekDepot on role assignments (ACM-247)
298 Oct 2026JonasHome depot routing and the 14-day job, from Claude Code's notes
3015 Oct 2026MarekNote on per-depot certificates and ACM-293
3122 Oct 2026MarekWaiting requests follow their depot's approver (ACM-320)