A former developer finished a three-month engagement eight months ago. Their GitHub access - including private repos, internal tooling, and staging environments - is still live. Nobody noticed. The IT team disabled the Okta account, so it looked handled. But that developer was never in Okta to begin with. They used a personal Gmail, provisioned directly by the lead engineer on day one.
This is what an "orphaned contractor account" actually looks like in the wild. Not a theoretical risk. Not an edge case. A completely predictable outcome of a process designed for employees - one that quietly fails every time it meets a contractor.
Contingent workers now make up roughly 30-40% of the U.S. workforce, according to the U.S. Government Accountability Office, and that number keeps climbing1that number is forecast to keep climbing. Yet most identity governance programs still assume the workforce is made entirely of employees with HRIS records. It isn't. The gap between how your tools model your workforce and how it actually looks is where orphaned accounts, overprovisioned access, and audit findings live.
This guide is a practical walkthrough - not a "why this matters" post (we covered that in our piece on why standard JML automation breaks for contractors). Here's the step-by-step process for fixing it: alternative identity sources, automated provisioning, time-bound access, and clean offboarding - even when no HR event ever fires.
The Core Problem: Your JML Process Was Designed for Employees
Most organizations run joiner-mover-leaver (JML) automation built on a single assumption: the HRIS is the source of truth. Employee gets hired -> HR creates a record -> a workflow fires -> access gets provisioned. Employee leaves -> HR closes the record -> SSO is disabled -> SCIM-connected apps get deprovisioned.
This works - when the person is in the HRIS.
Contractors usually aren't. They're tracked in procurement portals, staffing agency systems, manager emails, or spreadsheets. When a contractor's engagement ends, no HR record closes. No JML event fires. No automation runs. The contractor's Slack workspace, GitHub membership, Notion access, and Jira instance stay exactly as they were on day one.
The HRIS trap: Standard JML automation fires when an HR record changes. Contractors usually have no HR record - so no event ever fires, and no account ever gets deprovisioned automatically. This isn't a process failure. It's an architectural gap in how most IGA tools were designed.
This isn't an IT team failure. It's an architectural gap. The tooling was never designed to handle identities that exist outside the HRIS. Until you change the architecture - not just the process - you'll keep producing orphaned accounts on a schedule.
Before You Start: Assess Your Current Exposure
Before fixing the process going forward, know how exposed you are right now.
The 7-Step Process for Clean Contractor Lifecycle Management
Stop treating the HRIS as your only source of truth. Map every system where contractor identities actually live: procurement/vendor portals, project management tools (Jira, Linear), manager-initiated requests, contract management platforms (DocuSign, Ironclad), staffing agency rosters, and manual IT intake tickets. Each of these is a valid identity source - you just need to wire them in.
At the moment a contractor is onboarded, collect: contract start date, contract end date (mandatory), project scope (which tools, which repos, which channels), access approver (the sponsoring manager), and access level (least-privilege, not 'give them what the dev team has'). This metadata is what drives automated provisioning and clean offboarding later. Without it, you're already setting up an orphan account.
Route the access request through an automated workflow, not a ticket queue. The workflow should: check the contractor's role profile against a predefined entitlement catalog, provision only the specific apps and scopes needed (channel-level in Slack, repo-level in GitHub, page-level in Notion - not broad group assignments), and log every provisioning action with a timestamp and approver for audit trail purposes.
Every contractor account gets an automatic expiry date tied to the contract end date captured in Step 2. This is not optional and not a reminder email - it's a hard expiry enforced at the access layer. When the date arrives, access is revoked automatically across every connected app, whether it supports SCIM, has an API, or neither. No manual action required. No Slack reminder to 'please remember to deprovision.'
A contractor's role changes mid-project more often than you think. Enforce quarterly (or event-triggered) access reviews for all active contractor identities. Reviews should surface: unused entitlements (apps the contractor hasn't logged into in 30+ days), scope creep (access that widened beyond the original approval), and accounts that no longer map to an active contract or manager. Right-size permissions continuously - don't wait for offboarding to discover the sprawl.
You cannot wait for HR to fire a termination event that was never going to fire. Set up three parallel offboarding triggers: (1) Date-based: the contract end date from Step 2 fires an automatic full deprovision. (2) Manager-initiated: the sponsoring manager has a one-click offboarding action that removes all contractor access immediately. (3) Inactivity-based: if a contractor account shows zero activity for a configurable period (e.g., 30 days), it's automatically flagged for suspension and review. Any one of these three triggers should be sufficient to close all access - including apps outside SSO, apps without SCIM, and any direct credentials that were provisioned outside the main identity provider.
After offboarding fires, automatically generate a deprovisioning report: which accounts were closed, at what timestamp, across which apps. This report becomes your audit evidence. It answers the auditor's question - 'Can you confirm this former contractor no longer has access?' - in seconds, not weeks of manual log hunting. Store it alongside the original access approval as a complete identity lifecycle record.
The Offboarding Trigger Problem: Why "We Have a Process" Isn't Enough
Most teams, when asked about contractor offboarding, say some version of: "The manager emails IT when the contract ends." That's a process. It's not governance.
The difference matters. A process depends on someone remembering, acting, and following through - across every contractor, every project, every team, every time zone. Governance is automated, policy-driven, and doesn't depend on anyone's memory.
Here's how the most common offboarding triggers compare - and where each one breaks down:
| Offboarding Trigger | How It Works | What It Misses | Risk Level |
|---|---|---|---|
| HR termination event | HR closes record -> SSO disabled -> SCIM apps deprovisioned | Contractors with no HR record, direct logins, non-SCIM apps | 🔴 High for contractors |
| Manual IT ticket | Manager emails IT -> IT manually removes access app by app | Apps IT doesn't know about, after-hours departures, forgotten tickets | 🔴 High |
| SSO disable only | Okta/Entra account disabled -> SSO-gated apps blocked | Direct logins, apps outside SSO perimeter, non-SCIM integrations | 🟠 Medium (partial) |
| Date-based auto-expiry (contract end date) | Contract end date fires automatic deprovision across all connected apps | Only covers apps connected to the governance platform | 🟢 Low (with universal connectors) |
| Manager-initiated one-click offboarding | Manager triggers immediate full access removal from a single interface | Requires manager action - must be part of process design | 🟢 Low (with right tooling) |
| Inactivity-based suspension | No login for 30+ days -> account auto-suspended and flagged for review | Doesn't catch access that's still in use by an unauthorized party | 🟢 Low (as a safety net) |
The only approach that reliably closes access without a human action is date-based auto-expiry - with universal app coverage as the enforcement mechanism. Without universal connectors that reach every app in your stack (SCIM, API, or neither), time-bound access is just a policy on paper.
What "Alternative Identity Sources" Actually Means in Practice
If contractors don't live in your HRIS, you need to wire in the systems where they do live. Here's what that looks like across common contractor intake paths:
Procurement and Vendor Portals
Contractors hired through procurement flows (SAP Ariba, Coupa, ServiceNow vendor records) have a contract record with a start date and end date. Wire this system into your IGA platform as an identity source. A new vendor record triggers provisioning. A closed purchase order triggers offboarding.
Manager Attestation and IT Intake Forms
For contractors onboarded via manager request (a Jira ticket, a form, a Slack message), the intake form becomes the identity source - but only if you capture the right data upfront. A form that collects name, email, and "what they need access to" isn't enough. You need contract end date, sponsoring manager, and access scope. Anything less and you've onboarded someone you can't cleanly offboard.
Project Start/End Dates as Lifecycle Signals
For project-based contractors (common in tech, logistics, and consulting), the project itself is the lifecycle signal. When the project in Jira, Linear, or your PSA tool closes - or when a sprint milestone is reached - that event can trigger a contractor access review or expiry. This works especially well for contractors tied to a specific deliverable rather than an ongoing role.
Staffing Agency and Workforce Management Systems
If you use a vendor management system (VMS) or staffing platform, the contractor lifecycle often lives there first. An API connection between your VMS and your IGA platform means contractor onboarding and offboarding synchronize automatically - no manual handoff required.
The unifying principle: every intake path needs to produce the same three data points - start date, end date, and access scope - and feed them into a single governance layer that applies consistent policy regardless of how the contractor was sourced.
Fine-Grained Provisioning: The Difference Between "Access" and "The Right Access"
One of the most common mistakes in contractor onboarding is provisioning access by analogy: "Give them the same setup as the dev team." Fast? Sure. Correct? Never.
Contractors should get only the access their specific role and project require - at the most granular level your tools support. That means:
- Slack: specific channels, not workspace-wide access
- GitHub: specific repositories, not org-wide membership
- Notion: specific pages or databases, not full workspace access
- Jira: specific projects, not every project in the instance
- AWS/GCP: specific environments, not dev-or-prod-level broad roles
This isn't pedantry. It's the difference between a contractor whose access, if compromised, exposes one project - and one whose access exposes your entire engineering stack.
Most IGA tools stop at SCIM group assignments, which means fine-grained access at the channel, repo, or project level goes unenforced. Look for a platform with connectors that go deeper than SCIM - what some call "SCIM++" - so the policy you write matches the access actually granted.
The Three-Trigger Offboarding Model
Clean contractor offboarding doesn't rely on a single trigger. It uses three in parallel - any one of which should be sufficient to revoke all access:
Trigger 1 - Date-Based Expiry The contract end date, captured at onboarding, fires an automated full deprovision on the scheduled day. No human action required. This is the primary trigger.
Trigger 2 - Manager-Initiated Offboarding The sponsoring manager has a single-action offboarding button - in the governance platform, or surfaced via Slack/Teams - that immediately removes all contractor access. This handles early terminations, scope changes, and situations where the contract end date shifts unexpectedly.
Trigger 3 - Inactivity-Based Suspension If a contractor account shows zero login activity for a configurable period (30 days is a reasonable default), it's automatically flagged for suspension and sent to the sponsoring manager for review. This catches contractors who've informally stopped working but whose formal end date hasn't arrived - and accounts that were simply forgotten.
All three triggers need to deprovision access across every connected application - including apps outside the SSO perimeter, apps without SCIM support, and any direct credentials provisioned outside your main identity provider. An offboarding that removes Okta access but leaves GitHub, Notion, and the customer portal untouched is a partial offboarding - which is just an orphaned account in slow motion.
Audit Readiness: Closing the Loop on Every Contractor Lifecycle
When a compliance framework asks you to prove a contractor no longer has access, you need that proof in seconds - not reassembled from screenshots, access logs, and Slack histories over three weeks.
Every contractor offboarding event should automatically generate a deprovisioning record: a timestamped report listing every account closed, across every app, with the trigger that fired and the approver of record. This document, alongside the original access approval from onboarding, forms a complete identity lifecycle audit trail.
That audit trail answers questions like:
- "Who had access to this repository between January and March?"
- "Was contractor X still active on the day of the incident?"
- "Can you prove the former vendor's access was removed when the contract ended?"
These are real audit questions. The organizations that answer them confidently are the ones that automated the lifecycle - not the ones with the most comprehensive spreadsheet.
For a broader look at how identity governance supports compliance readiness across frameworks like SOC 2, ISO 27001, and DORA, see our guide to regulatory-ready identity governance.
Where Universal Connectors Change the Equation
Everything in this guide depends on one underlying capability: the ability to provision and deprovision access across every app in your stack - not just the SCIM-friendly ones.
According to research cited by C1, 89% of organizations have integrated fewer than half of their applications with their IGA solution - leaving most of the environment ungoverned. Contractors often access the exact tools that sit outside the SSO perimeter (the project management tool, the analytics dashboard, the design platform on a free tier), making this gap even wider.
Iden's universal connectors reach 175+ applications, including apps without SCIM or APIs - no enterprise plan upgrades required to unlock automation. That means contractor access, including the "silly SaaS apps" that somehow became critical to your stack, is governed from day one with the same lifecycle automation that applies to your SCIM-connected core tools.
When a contractor's end date arrives, offboarding fires across every connected application automatically. Not just Okta. Not just the apps with SCIM. All of them. That's what zero missed offboarding actually requires.
Key Takeaways
- Contractors don't have HRIS records - JML automation that depends on HR events will never fire for them. You need alternative identity sources.
- Capture three data points at onboarding: contract end date, sponsoring manager, and granular access scope. Without these, clean offboarding is impossible.
- Use three parallel offboarding triggers: date-based expiry, manager-initiated removal, and inactivity-based suspension. Any one should be sufficient.
- Fine-grained access matters: provision at the channel, repo, and project level - not broad group assignments that outlast the engagement.
- Universal connector coverage is the enforcement layer: time-bound access policies are only as strong as your ability to enforce them across 100% of your stack.
- Auto-generate deprovisioning records: your audit trail should be automatic, not reconstructed manually when an auditor asks.
Frequently Asked Questions
What if contractors are onboarded by different teams and there's no central intake process?
This is the most common situation. The fix isn't to mandate a single intake channel overnight - it's to wire multiple intake channels into a single governance layer. Whether the request comes from a Jira ticket, a manager-submitted form, a procurement portal, or a staffing agency API, your IGA platform should be able to ingest that request, extract the lifecycle metadata (start date, end date, scope), and apply the same policy-driven provisioning logic regardless of source.
Can we enforce time-bound access for apps that don't support SCIM or have no API?
Yes - but only if your governance platform has universal connector coverage. Most IGA tools stop at SCIM-enabled apps. Iden's universal connectors reach apps with SCIM, apps with APIs, and apps with neither, using agent-based or UI-driven automation to provision and deprovision access regardless of what the application natively supports. Time-bound access is only as good as your ability to enforce it across 100% of the stack.
What's the minimum metadata we need to collect at contractor onboarding to enable clean offboarding?
At minimum: contract end date, sponsoring manager (the offboarding approver), and the specific access scope (which apps, which repos, which channels - not just a role group). With these three data points, you can build a fully automated lifecycle that provisions on day one and deprovisions on day last without any manual intervention.
How do we handle contractors who extend their contract or come back for a second engagement?
Extensions should trigger a re-attestation workflow - the sponsoring manager approves a new end date, and the expiry is updated automatically. For returning contractors, treat them as a net-new provisioning cycle: re-verify their access needs, apply least-privilege from scratch, and set a new expiry. Avoid the temptation to simply 'reactivate' an old account, which often carries stale permissions from the previous engagement.
How do we prove to an auditor that a contractor's access was fully removed?
Your IGA platform should auto-generate a deprovisioning record at the moment offboarding completes: a timestamped report listing every account closed, in every app, at what time, triggered by which event (date-based, manager-initiated, or inactivity). This record - alongside the original access approval - forms a complete identity lifecycle audit trail. No screenshots. No manual log correlation. No weeks of prep.


