SAML answers “can this person sign in”. SCIM answers “should this person exist here at all”.
They are frequently conflated, and the difference is the entire deprovisioning problem: an enterprise with SAML but no SCIM has a leaver whose identity provider account is disabled and whose GitHub membership persists indefinitely, until somebody notices.
The short answer
Section titled “The short answer”SCIM provisions and deprovisions accounts from your identity provider. Assigning a user to the GitHub application in the IdP provisions them; unassigning them deprovisions them.
Assigning a group provisions all its members.
IdP groups can map to GitHub teams, and GitHub documents that this is used to manage both team membership and organisation membership.
Deprovisioning suspends. The account cannot access the enterprise. GitHub distinguishes soft deprovisioning, from which an account can be unsuspended via SCIM, from hard deprovisioning, from which it cannot.
Deprovisioning also removes team membership. GitHub documents that on deprovisioning, a user is hidden from SCIM-provisioned IdP groups and removed from mapped teams even if they are still in the group on the IdP side — and removed from organisations where organisation membership is managed by IdP groups.
REST-API-based SCIM provisioning is also documented, for identity providers without a native integration.
What provisioning gives you
Section titled “What provisioning gives you”Joiners appear without a ticket. Somebody joins, the directory adds them to the engineering group, they have GitHub access on their first day.
Movers change automatically. Somebody moves from the payments team to the platform team; the directory group changes; their GitHub team membership follows. This is the case manual processes always miss, because nobody thinks of a role change as an access change.
Leavers lose access. The one everybody focuses on, and rightly — it is the difference between an access review that finds departed employees and one that does not.
The directory becomes the single source of truth, which is the actual goal. Every question about who has access has one answer, in one place, maintained by a process that already exists.
What deprovisioning actually does
Section titled “What deprovisioning actually does”Precision here matters, because the behaviour is more specific than “removes them”.
The account is suspended. It cannot access the enterprise.
Team membership is removed — and notably, GitHub documents that this happens even if the user is still in the corresponding group on the IdP side. The deprovisioning action wins.
Organisation membership is removed where organisation membership is managed by IdP groups.
Soft versus hard. A soft-deprovisioned account can be unsuspended via SCIM; a hard-deprovisioned one cannot. The distinction matters for someone leaving and returning, and for correcting a mistaken deprovisioning.
What it does not do: it does not remove outside collaborator access, because collaborators are not provisioned by SCIM. It does not revoke credentials held by machine identities the person created. And it does not reach clones already on their machine.
Designing the directory groups
Section titled “Designing the directory groups”Because mapped groups become access control, the group design deserves more attention than it usually gets — and it is frequently the actual work of a SCIM rollout.
Groups should express a durable fact about a person, not a permission. “Member of the payments team” is durable. “Can deploy to production” is a permission, and encoding it as a directory group means the directory now holds authorisation decisions it was not designed to hold.
One group per team, matching how the organisation actually describes itself. If the org chart says there is a payments team, there should be a payments group, and it should contain the payments team.
Include a group for the whole engineering population. It provisions accounts for everybody and grants the baseline; team groups add specificity on top.
Avoid groups that encode seniority or role unless the access genuinely differs. “Senior engineers” as an access group means promotions become access changes, which is a bad coupling.
Decide who maintains them. Frequently the identity team owns group existence and the managers own group membership. That split works, provided the managers know they are now making access decisions.
Handle the special cases explicitly. Contractors, interns, secondments and dual-role people all exist, and if the group design has no answer for them, somebody will make a manual GitHub change instead — which is exactly the leak SCIM was meant to close.
Expect this to take longer than the technical configuration. Getting SCIM working is a day. Getting the directory groups to reflect the organisation is a project, and it is the part that determines whether the outcome is useful.
Group-to-team mapping
Section titled “Group-to-team mapping”The design decision that determines whether SCIM is useful or merely present.
Map directory groups to GitHub teams, and team membership becomes a directory concern.
Which means the quality of your directory groups becomes the quality of your access control. This is frequently a surprise, because directory groups have often been maintained casually — nothing important depended on them until now.
Audit the groups before mapping them. A group named for a team that reorganised two years ago, still containing forty people, is about to become a GitHub team with forty members.
Map at the right granularity. A group per team is right. A group per repository is unmaintainable. A single group for all of engineering gives you provisioning but no differentiation.
Nested groups behave according to your IdP’s semantics, not GitHub’s, and this is worth testing rather than assuming — the behaviour differs between providers.
Keep some teams unmapped deliberately. A team representing an on-call rotation or a temporary working group may be better maintained in GitHub than in the directory, and mixing mapped and unmapped teams is fine as long as it is documented which is which.
SCIM under EMU versus SCIM alongside personal accounts
Section titled “SCIM under EMU versus SCIM alongside personal accounts”The same protocol, two materially different situations.
With Enterprise Managed Users, SCIM is the only way accounts exist. GitHub creates the managed user account from the IdP identity; there is no pre-existing personal account to link. Usernames are generated by normalising an IdP identifier, and the profile name and email come from the IdP with the user unable to change them.
Which makes the model complete by construction. There is no account outside SCIM, because there is no way to create one.
With personal accounts, SCIM manages membership of an account the user owns. They had a GitHub account before; they will have it after. SCIM controls whether it is a member of your organisations, not whether it exists.
The practical difference at deprovisioning: under EMU the account is suspended and is useless. With personal accounts, the account persists and simply loses membership — which is correct, since it is their account, and it means the person retains their GitHub identity, their contribution history and their public work.
A consequence worth knowing: under EMU, username generation from a normalised IdP identifier can produce conflicts if the distinguishing part of an identifier is normalised away. This is an edge case that surfaces with unusual identifier schemes, and it is worth testing with real directory data rather than sample data.
Choose based on the identity model, which is covered in enterprise IAM. SCIM’s role differs, but the operational disciplines below — reconciliation, deprovisioning timing, leak management — apply to both.
Reconciliation
Section titled “Reconciliation”The operational practice that separates SCIM that works from SCIM that appears to work.
Compare the two sides regularly. Directory group membership versus GitHub team membership. Differences mean either the synchronisation is failing or somebody has been making manual changes.
Manual changes are the common cause. An administrator adds somebody to a team directly to unblock them, and that membership now exists outside the directory — invisible to the leaver process, invisible to access reviews.
Report the differences; do not silently correct them. A reconciliation job that overwrites without reporting hides the fact that a manual process is happening.
Check the member count. Total GitHub members versus total users assigned to the GitHub application in the IdP. A persistent gap is a population outside the model.
Check for members with no linked identity. Under SAML enforcement this should be zero; a non-zero value under SCIM is a stronger signal, because SCIM should have provisioned them.
Time the deprovisioning. From directory removal to GitHub suspension. This is the number an auditor will ask for, and it is worth measuring rather than asserting.
What SCIM is worth
Section titled “What SCIM is worth”The case, for when somebody asks why this is a quarter of work.
Deprovisioning latency goes from indefinite to minutes. Without SCIM, a leaver’s GitHub access ends when a human remembers. The honest measure of that in most enterprises is months, and for some accounts, never. This is the single largest access-control improvement available.
Access reviews become tractable. Reviewing GitHub membership means reviewing directory groups, which the organisation already reviews. Without SCIM it is a separate exercise against a separate list.
Joiner friction disappears. New engineers are productive on day one rather than after a ticket. Small per person, meaningful across a year of hiring.
Movers are handled. The case manual processes universally miss.
The evidence is produced as a by-product. “Show that access is removed promptly on termination” is answered by the deprovisioning timing, which SCIM generates.
The count of members outside the directory becomes visible, and it is invariably a surprise. That number, produced during rollout, is frequently the strongest argument for the work — retrospectively.
Against which: the directory group work is real, and it is where the effort actually goes. Budget for it explicitly rather than discovering it.
Rolling out SCIM
Section titled “Rolling out SCIM”-
Get SAML working and enforced first. SCIM without linked identities is provisioning into a vacuum.
-
Audit the directory groups you intend to map. Fix the stale ones before mapping, not after.
-
Start with provisioning only. Assign the GitHub application to a small group and confirm accounts appear correctly.
-
Test deprovisioning deliberately. Remove a test user, confirm the account suspends, confirm team membership is removed, and time it.
-
Test the soft and hard deprovisioning behaviours if both apply to your configuration. Knowing which one your process produces matters when somebody returns.
-
Map groups to teams incrementally, a few teams at a time, reconciling after each.
-
Reconcile weekly for the first quarter, then monthly. The early weeks find the configuration errors.
-
Then stop making manual membership changes, and mean it. A single exception undermines the model.
When synchronisation fails
Section titled “When synchronisation fails”SCIM failures are quiet. Nothing errors visibly; things simply stop happening.
The identity provider stops sending. An expired credential on the IdP’s GitHub application, a configuration change, an upgrade. Provisioning stops, deprovisioning stops, and nobody notices until a joiner does not appear — or worse, until a leaver is found still to have access.
Individual operations fail. A user whose attributes do not meet requirements, a group that grew too large, a name collision. The rest of the synchronisation continues, so the overall picture looks healthy.
The mapping breaks. A group is renamed on the IdP side and the mapping references the old name. Nothing errors; the group simply has no members from GitHub’s perspective.
The symptom is always the same: GitHub state diverging slowly from directory state, with no alert.
Which is why reconciliation is not optional. It is the only detection mechanism for a class of failure that is otherwise invisible.
Two things to monitor:
- Provisioning activity. A day with zero SCIM events in an enterprise that normally has several is a signal. This is visible in the audit log.
- Divergence. The reconciliation report itself, with an alert when the count exceeds a threshold.
Check the IdP side too. Most identity providers log provisioning attempts and their outcomes, and that log is where the specific error lives when a single user fails.
Test after every IdP change. Your identity team will upgrade and reconfigure without knowing GitHub depends on the integration. A test account that gets provisioned and deprovisioned on a schedule is a cheap canary.
Where the model leaks
Section titled “Where the model leaks”Outside collaborators. Not provisioned, not deprovisioned, not covered. The largest leak in most enterprises.
Machine accounts. If a bot has a GitHub account with somebody’s email, SCIM may deprovision it when that person leaves — or may not provision it at all, leaving it outside the model. Neither is good. See machine identities.
Manual team additions. Every one is a membership outside the directory.
Repository-level collaborator grants. A person granted access directly on a repository, rather than through a team, keeps that grant when their team membership changes.
Enterprise owners. Frequently managed manually, and frequently stale.
Credentials. SCIM removes access; it does not revoke tokens the person created for automation that now belongs to nobody.
Each leak is a category, not an accident. Fixing them means covering the category — a review process for collaborators, a naming and ownership convention for machine accounts, a policy against direct grants — rather than fixing instances.
The offboarding sequence
Section titled “The offboarding sequence”SCIM handles the main path. A complete offboarding covers what it does not, and this is worth writing as a checklist because it will be executed by somebody under time pressure.
-
Remove from the directory. SCIM suspends the account and removes mapped team memberships. This is the automatic part.
-
Revoke sessions and authorised credentials. SCIM’s action is membership; explicit revocation closes the credential path. See SAML SSO for the sequence.
-
Check for outside collaborator grants. Not covered by SCIM, and a separate search.
-
Check for direct repository grants. A person granted access on a repository rather than through a team.
-
Reassign machine identities they created. Tokens, deploy keys and app installations they own. This is the step that breaks production three weeks later when nobody does it.
-
Check enterprise and organisation owner lists. Frequently managed manually.
-
Reassign repository ownership. Any repository whose owning-team property names them individually. See repository lifecycle governance.
-
Record what was done, because this is the evidence an access-management control objective asks for.
Steps 3 through 7 are the ones that get skipped, and they are where residual access lives. Automating the detection — a report of everything associated with a departing person — makes the manual part tractable even when the removal itself stays manual.
Run the same checklist for movers. Somebody changing teams should lose the old team’s access, and SCIM handles that only for mapped teams. The direct grants and machine identities persist.
Reporting on the model
Section titled “Reporting on the model”Four artifacts worth producing on a schedule, each answering a question somebody will ask.
The divergence report. Directory group membership versus GitHub team membership, per mapped team, with counts and names. Weekly at first, monthly once stable. Answers: is synchronisation working?
The uncovered population report. Members with no linked identity, outside collaborators, machine accounts. Answers: who is outside the model?
The deprovisioning timing report. For each departure in the period, time from directory removal to GitHub suspension. Answers: how quickly does access end?
The manual change report. Team membership changes made in GitHub rather than through SCIM, from the audit log. Answers: is anybody working around the model?
Send them to different people. Divergence to whoever operates the integration. Uncovered population to whoever owns access risk. Timing to whoever answers the auditor. Manual changes to the platform team, because each one is a signal that something the model does not support is being attempted.
Keep the historical reports. “Who had access on this date” is answerable from a point-in-time export and unanswerable from current state, and it is the question that arrives after an incident. See compliance evidence.
Common mistakes
Section titled “Common mistakes”Deploying SCIM before SAML. Provisioning without linked identity.
Mapping stale directory groups. You inherit their staleness.
Never reconciling. Manual changes accumulate invisibly.
Silently correcting differences. Hides the manual process rather than fixing it.
Assuming SCIM covers collaborators. It does not.
Manual team additions after rollout. Each one is a hole in the model.
Not testing deprovisioning. The behaviour is specific and worth confirming.
Not measuring deprovisioning time. It is the number that gets asked for.
Treating a group per repository as the model. Unmaintainable at any scale.
SCIM through the REST API
Section titled “SCIM through the REST API”Not every identity provider has a native GitHub integration, and GitHub documents REST-API-based SCIM provisioning for that case.
The protocol is the same. SCIM is a standard; GitHub exposes endpoints for creating, updating and deleting provisioned identities.
What changes is who drives it. Instead of the identity provider’s built-in connector, something you build or buy makes the calls — an identity management platform, a middleware layer, or a script.
Which shifts responsibility onto you for the parts a native connector handles: retry on failure, rate limiting, ordering, and reconciliation of partial failures. These are not hard individually and they are easy to omit.
Two properties matter most:
Idempotency. Running the synchronisation twice must produce the same result as running it once. A provisioning system that creates duplicates on retry will create duplicates, because retries happen.
Auditability. Every operation should be logged on your side as well as GitHub’s, because when a discrepancy appears you will need to know what your system attempted, not only what GitHub recorded.
Treat the credential carefully. Whatever authenticates the provisioning calls can create and remove enterprise members. It belongs in a secret manager, scoped as narrowly as the API permits, rotated on a schedule, and never in a repository.
And test the failure paths. What happens when GitHub is unavailable mid-synchronisation, when a user fails validation, when the credential expires. A provisioning system that only works on the happy path will diverge silently the first time it does not.
Mental model
Section titled “Mental model”SCIM makes GitHub membership a derived fact. The directory decides; GitHub follows. Every membership that exists outside that derivation — a collaborator, a manual addition, a direct repository grant — is a fact the directory does not know, and therefore a fact your leaver process cannot act on.
What you learned
Section titled “What you learned”- SCIM provisions and deprovisions accounts; assigning a group provisions all its members
- Deprovisioning suspends the account, with soft deprovisioning reversible via SCIM and hard deprovisioning not
- Deprovisioning removes mapped team membership even if the user is still in the IdP group
- Organisation membership is removed where it is managed by IdP groups
- Group-to-team mapping makes directory group quality the determinant of access control quality
- Reconciliation should report differences rather than silently correcting them
- Outside collaborators, machine accounts, manual grants and credentials all sit outside SCIM
- SAML must be working and enforced before SCIM is useful
- Deprovisioning time is the metric to measure and the one auditors ask for
Exercise
Section titled “Exercise”Use an enterprise with SCIM configured, or reason from documentation.
-
List the directory groups mapped to GitHub teams. For one, compare both memberships.
-
Count differences. Predict: are there any, and what caused them?
-
Count total GitHub members versus users assigned to the GitHub application in the IdP.
-
Find any GitHub team with members not present in its mapped group. How did they get there?
-
Test deprovisioning with a test account. Time it from IdP removal to GitHub suspension.
-
List outside collaborators. Predict: how many are covered by any identity control?
-
Find a machine account with a person’s email attached. What happens when that person leaves?
-
Write down the reconciliation report you would want, and who should receive it.