GitHub Well-Architected for Financial Services, Part 2: Identity and Access Management
10 Nov 2026 github well-architected financial-services compliance security governance identity access-management least-privilegeThis post is part of the GitHub Well-Architected for Financial Services series.
| Previous: Part 1: Platform Foundation and Data Boundaries | Next: Part 3: Source Code Management and Change Control |
In this post
- Leadership brief
- WAF outcomes for identity and access
- Design a usable joiner, mover, and leaver journey
- The requirements
- Deployment model differences
- Account model: data residency with EMU by default
- Authentication protocol and conditional access
- MFA: where it actually happens
- Provisioning, deprovisioning, and team sync
- Least privilege: base permissions and roles
- Privileged and break-glass accounts
- Personal access tokens
- GitHub Apps, OAuth apps, and deploy keys
- Operate service identities throughout their lifecycle
- SSH certificate authorities
- Network and session controls
- Credential revocation and incident response
- Access reviews and the evidence pack
- Control matrix
- Summary and key takeaways
- References
Leadership brief
- Why it matters: Excess access exposes systems and information; slow access prevents people from doing their jobs. Departures must not leave a way back in.
- Your decision: Require access based on job responsibilities, prompt removal when responsibilities end, and separate approval for powerful administrative permissions.
- Accountability: Business owners approve need; the identity and platform teams manage access for employees, contractors, and automated services.
- Ask for: Recent examples showing access granted on time, removed after departure, and independently reviewed for people with sensitive permissions.
Access management must give people the access they need and remove it when that need ends. A team move should not leave old administrative permissions behind, and an employee departure should not break a release process that depends on their personal credentials.
This chapter follows the identity lifecycle for employees, contractors, and automated services: establishing identity, granting access, reviewing privileges, and verifying removal. It connects those responsibilities across browser sessions, Git credentials, and integrations so access works as a supported service rather than a collection of disconnected settings.
Applicability: The regulatory mappings identify requirements these controls can support; they do not establish compliance or certification. Compliance confirms scope and exemptions. Apply Part 1’s deployment decision and GA production baseline. GHES guidance targets the 3.22 GA release series. Preview features are excluded from production. Validate GA availability and applicable terms before relying on the controls.
WAF outcomes for identity and access
| Pillar | Apply it here | Verify the engineering outcome |
|---|---|---|
| Governance | Assign entitlement owners, service sponsors, exception authority, and lifecycle deadlines. | Every grant has a current purpose and an accountable reviewer; overdue removals are incidents. |
| Architecture | Design IdP, SCIM, Git, app, network, and emergency access as connected paths. | Joiner, mover, leaver, and IdP-failure tests work end to end. |
| Application Security | Use least privilege, phishing-resistant MFA, governed credentials, and prompt revocation. | Allowed use succeeds; direct-access bypass and revoked credentials fail. |
| Collaboration | Offer team-based access across business lines with explicit information barriers. | An approved library contributor reaches maintainers without receiving unrelated banking permissions. |
| Productivity | Publish access bundles, setup instructions, approval routes, and actionable error guidance. | Measure time to first useful contribution, access-request rework, and avoidable support demand. |
Use the WAF governance checklist to connect policy with operating practice. Tight access that nobody can use drives workarounds; fast access with no removal path creates a different incident.
Design a usable joiner, mover, and leaver journey
What: access as a supported service
Define approved access bundles by engineering job and business scope. A payments contributor, infrastructure maintainer, shared-library reviewer, and release operator need different rights.
Bundles are institution-maintained mappings to IdP groups, GitHub teams, and roles, not a new GitHub feature. Keep privileged administration and emergency elevation separate from ordinary development.
Why: remove waiting and accidental privilege
Onboarding should produce working access and clear instructions. A role change should remove obsolete grants, not stack new ones on top. Offboarding should end access without breaking unrelated release automation.
The provisioning controls below implement removal deadlines and failure handling. A request portal alone does not.
How: make the lifecycle concrete
| Event | Supported path | Completion condition |
|---|---|---|
| Join payments engineering | Manager requests the job bundle; repository/business owner approves scope; IAM provisions the linked group | Engineer can authenticate, clone an approved repo, run safe tests, and find contribution instructions |
| Move from banking applications to infrastructure | Review old and new entitlements together; approve any time-limited overlap | New access works, old access is removed, and any overlap has an owner and expiry |
| Contribute to a shared library | Maintainer sponsors scoped team access under the approved collaboration model | Contributor can propose and test a change without accessing restricted consumer repositories |
| Join as a vendor | Named business sponsor approves repositories, role, and end date | Guest/outside access matches the contract and is scheduled for removal |
| Leave the institution | Trusted HR event triggers immediate removal and credential remediation | GitHub denies access and dependent service identities are separately reviewed |
For each bundle, publish the approver, expected turnaround, support route, required workstation setup, and allowed credential methods. Include hostnames for the actual environment; GHE.com instructions that still say github.com make a surprisingly effective onboarding blocker.
Measure readiness, not just account creation. A new banking-app engineer should complete a small documentation PR using synthetic test data. That validates authentication, team grants, Git access, signing setup, and review routing together.
On a move or departure, inventory independent app installations, deploy keys, scheduled jobs, and credential custody. Transfer legitimate service ownership through review; do not copy the departing person’s token into a shared vault and call it a service account.
Set onboarding and ordinary-change service objectives with the consuming teams. Do not treat those targets as grace periods for leavers: the immediate-removal requirements below still apply.
Verify: test the whole lifecycle
Monthly, review request-to-ready time, rejected/reworked requests, direct-grant exceptions, and the age of unresolved access failures by risk tier. Track movers as well as joiners and leavers.
Have IAM and a delivery team test a new joiner, a cross-business-line move, an expiring vendor grant, and a failed provisioning event. Retain the successful task and denied-access results. Include a privileged payments or release-operations case in the risk-based sample.
The requirements
Use the compliance crosswalk for applicability. This part expands PCI account-lifecycle and MFA citations; institutional policy cannot lower the applicable legal or contractual floor.
| ID | Regulation and citation | What it asks for (paraphrase) |
|---|---|---|
| US-FFIEC-INFOSEC | FFIEC IT Examination Handbook, Information Security booklet | Examiner framework for an information security program, including controls that protect confidentiality, integrity, and availability. |
| US-GLBA-314 | GLBA Safeguards Rule, 16 CFR Part 314 | A written, risk-based security program that includes access controls. |
| US-OCC-30-AppB | Interagency Guidelines, 12 CFR Part 30 Appendix B | A comprehensive written security program for OCC-regulated banks, including access controls. |
| US-NYDFS-500.7 | 23 NYCRR §500.7, Access Privileges and Management | Least-privilege access, periodic review, and removal of unnecessary privileged accounts. |
| US-NYDFS-500.12 | 23 NYCRR §500.12, Multi-Factor Authentication | MFA for any individual accessing any covered information system, subject to applicable exemptions; §500.12(b) permits CISO-approved reasonably equivalent or more secure compensating controls, reviewed at least annually. |
| US-NYDFS-500.14 | 23 NYCRR §500.14, Training and Monitoring | Monitoring for unauthorized access or tampering. |
| CA-OSFI-B13 | OSFI Guideline B-13, Technology and Cyber Risk Management | Risk-based management of technology and cyber risk, including cyber security controls. |
| PE-SBS-504 | Resolución SBS N° 504-2021 (Peru) | The information security and cybersecurity management framework your access controls roll up to. |
| AR-BCRA-A7724 | BCRA Comunicación “A” 7724 (Argentina) | Minimum requirements for governing technology and information security risk. |
| EU-DORA-RTS-ACCESS | Commission Delegated Regulation (EU) 2024/1774, Arts. 20-21 | Identity management (unique identity per person or system) and access control policies built on it. |
| INT-PCI-7.2 | PCI DSS v4.0.1, Req. 7.2 | Need-to-know access by job function, a documented access model, and periodic review. |
| INT-PCI-8.2.5 | PCI DSS v4.0.1, Req. 8.2.5 | Revoke access for terminated users immediately. |
| INT-PCI-8.2.6 | PCI DSS v4.0.1, Req. 8.2.6 | Remove or disable inactive user accounts within 90 days of inactivity. |
| INT-PCI-8.3 | PCI DSS v4.0.1, Req. 8.3 | Strong authentication for users and administrators. |
| INT-PCI-8.4 | PCI DSS v4.0.1, Req. 8.4 | MFA for applicable non-console administrative access into the CDE, all access into the CDE, and remote network access that could access or impact the CDE; apply the standard’s specific exceptions. |
| INT-SWIFT-CSCF-OVERVIEW | SWIFT Customer Security Controls Framework (overview) | “Know and limit access” is one of three core objectives. |
| INT-NIST-CSF | NIST Cybersecurity Framework 2.0 | Reference taxonomy; identity and access controls live under the Protect function. |
Auditors test: policy adequacy, configuration, operating effectiveness, and timely remediation. A checkbox is not sustained operation.
Deployment model differences
Part 1 owns deployment selection. Here are the IAM consequences.
| Topic | GHEC (personal accounts) | GHEC with EMU | GHEC with data residency (GHE.com) | GHES |
|---|---|---|---|---|
| Account owner | The developer | Your IdP | Your IdP (EMU is required) | Your instance |
| Authentication | GitHub login, then SAML SSO | SAML, or OIDC with Entra ID | Same as EMU | Built-in, or SAML, LDAP, or CAS |
| Provisioning | Org-level SCIM only; grants org access, not accounts | SCIM required | Same as EMU (SCIM URL is on your SUBDOMAIN.ghe.com) |
JIT, plus optional SCIM with SAML |
| MFA | GitHub 2FA (enforceable) plus IdP MFA | IdP only | IdP only | Instance 2FA for built-in/LDAP; IdP for SAML/CAS |
| Conditional access | GitHub IP allow list | IdP CAP via OIDC (Entra ID) or GitHub IP allow list | Same as EMU | Your network perimeter |
| External collaborators | Outside collaborators on repositories | Guest collaborators, provisioned via IdP | Same as EMU | Local or IdP accounts |
| Enterprise custom roles | Public preview | Public preview | Public preview | Public preview in 3.22 |
GHE.com specifics: the setup user’s shortcode is randomly generated, and API, SCIM, and SSH endpoints all move to your subdomain. Update IdP apps, scripts, and allow lists accordingly (GHE.com feature overview).
Account model: data residency with EMU by default
Identity consequence: the Part 1 default includes EMU. The IdP owns managed human accounts; the setup user remains a GitHub-credential exception. PATs and SSH remain non-interactive access paths.
Managed-user restrictions limit organizations to private/internal repositories and prevent participation outside the enterprise. Open-source work needs separate personal accounts and an approved separate environment. Existing personal-account enterprises use the SAML/SCIM controls below, not EMU assumptions.
How:
- Use the deployment and region approved in Part 1’s decision record.
- Follow Getting started with Enterprise Managed Users: secure the setup user, configure authentication, then configure SCIM.
- Use one partner IdP for fully supported authentication and provisioning. Standards-conformant alternatives have limited support, not a universal prohibition.
Verify:
- Enterprise settings show “managed users” as the enterprise type. Screenshot it once for the control file.
- Reconcile IdP assignments with the enterprise membership export daily and certify quarterly. Explain setup, guest, suspended, and service identities rather than expecting raw counts to match without normalization.
Authentication protocol and conditional access
What we do:
- Entra ID shops: prefer OIDC for authentication. Use GitHub’s GA enterprise IP allow list by default; selecting the IdP CAP list is a separate, reviewed network decision.
- Okta or PingFederate: use SAML, and pair it with GitHub’s IP allow list.
- Personal-account enterprises: configure SAML at the enterprise level, not per organization.
Why: GitHub recommends OIDC for Entra ID. That does not mean CAP protects every credential or continuously enforces MFA and device posture.
With per-organization SAML, personal-account members can access internal repositories without SAML. Enterprise SAML closes that gap and prevents org overrides.
What CAP does and does not do (source):
| CAP behavior | Detail |
|---|---|
| Enforces | IdP IP conditions for documented user interactions and PAT/SSH authentication; extended web-session protection requires the preview option |
| Does not enforce | Device compliance conditions |
| MFA | Enforced only at IdP sign-in |
| Skips | Deploy keys, and GitHub Apps acting as themselves (installation tokens) |
| Preview | Extended CAP protection for web sessions is in public preview |
| Can block (plan for it) | PATs used in Actions, Codespaces, github.dev, and Enterprise Importer runs from dynamic IPs |
Preview web-session CAP is excluded from production. Use GitHub’s GA IP list with OIDC, or assess the GA IdP list’s residual coverage through the separate network decision above without enabling preview extensions. Block routes that cannot meet mandatory network requirements. Device posture needs verified endpoint/IdP controls, not GitHub CAP.
How:
- Follow Configuring OIDC for Enterprise Managed Users. Each Entra tenant supports only one OIDC integration. A second enterprise from the same tenant must use SAML.
- Session length is an IdP decision. The default OIDC token lifetime is one hour and is tuned with an Entra token lifetime policy. For SAML, GitHub’s default login period is 24 hours unless your IdP specifies otherwise.
- For personal-account enterprises, follow Configuring SAML SSO for your enterprise.
Verify:
- Audit log:
business.enable_saml,business.disable_saml, andbusiness.update_saml_provider_settingsshould be rare and tied to change tickets. Alert on any of them. - Keep an export of the CAP policy that targets the GitHub app in your IdP, reviewed with your other CAP policies.
MFA: where it actually happens
What we do: MFA is enforced at the IdP with phishing-resistant methods. Where GitHub itself holds credentials, we also require GitHub 2FA with secure methods only.
Why: the current NYDFS 500.12(a) requires MFA for any individual accessing any information systems of a covered entity, subject to exemptions. For an entity qualifying under 500.19(a), MFA remains required for remote access to its systems, remote access to third-party applications exposing nonpublic information, and privileged accounts except service accounts that prohibit interactive login. Other applicable 500.19 exemptions require a documented determination.
Under 500.12(b), if the entity has a CISO, the CISO may approve reasonably equivalent or more secure compensating controls in writing, reviewed periodically and at least annually. This is not a generic waiver for difficult integrations. PCI MFA scope comes from 8.4, not 8.3 alone, and must be reviewed separately.
The DFS MFA FAQs allow SSO token sharing without a fresh challenge, but prohibit direct application/API bypass. PATs are not universal CISO exceptions. Retain each path’s MFA chain or applicable approved compensating controls.
Inventory human PAT/SSH paths separately from non-interactive service identities, installation tokens, deploy keys, and the setup user. Test issuance, SSO authorization, network restrictions, expiry, revocation, and direct API/Git access. A short-lived token or IP restriction alone is not MFA. Block any required-MFA path that has neither adequate MFA nor lawful, approved compensating controls.
| Model | Where MFA lives | What to configure |
|---|---|---|
| EMU / GHE.com | IdP only. Managed users have no password or 2FA stored on GitHub. | IdP MFA policy for the GitHub app. GitHub 2FA on the setup user. |
| GHEC personal accounts | Both: GitHub login (2FA), then IdP (SAML) | Enterprise policy to require 2FA and require secure methods. Secure methods are passkeys, security keys, authenticator apps, and GitHub Mobile. SMS is out. |
| GHES with SAML or CAS | IdP only. Org-level 2FA enforcement is not available. | IdP MFA policy. |
| GHES with built-in or LDAP | The GHES appliance | Require 2FA in every organization. |
Heads-up: requiring 2FA removes outside collaborators (bots included) who lack it. Announce first, then flip it.
Verify:
business.enable_two_factor_requirementandorg.enable_two_factor_requirementevents exist;*.disable_two_factor_requirementevents should never appear without a ticket.-
For personal-account organizations, use the organization members API to identify members without two-factor authentication and those using insecure methods. Inspect both populations with organization-owner visibility, reconcile them to the complete membership inventory, and route exceptions to the access owner. An empty response proves nothing until permissions and collection completeness are verified.
- The enterprise membership export includes each user’s 2FA status and how secure it is. Attach it to the quarterly review.
- For EMU, your evidence is the IdP’s MFA policy plus IdP sign-in logs for the GitHub app.
- Keep an authentication-path matrix with identity type, information system/CDE scope, factors, SSO/token linkage, bypass tests, exception basis, approval owner, and annual review date. Test direct API and Git paths, not just browser sign-in.
Provisioning, deprovisioning, and team sync
What we do: Provision humans through IdP assignments and repository access through linked teams, not manual grants. Treat group mappings as maintained service configuration with named business owners. The setup-user exception is governed below.
Why: US-NYDFS-500.7, INT-PCI-7.2, and EU-DORA-RTS-ACCESS need timely removal. Without SCIM for organizations, disabling a personal-account user’s IdP access does not remove org membership; authorized tokens can outlive sessions.
How (EMU):
- Configure SCIM. GitHub invalidates sessions and disables the account when it receives the deprovisioning request, not merely when an HR or IdP record changes. Measure end-to-end propagation.
- Link teams to IdP groups. Adding a user to a linked team adds them to the org; removing the link removes org membership gained that way. GitHub runs a daily reconciliation job as a backstop.
- Grant repository access to those teams, never to individuals. Linked teams cannot have maintainers, which is a feature: membership changes go through the IdP.
How (GHEC personal accounts):
- Enable org-level SCIM (Entra ID, Okta, or OneLogin). SCIM is not available at the enterprise level without EMU.
- Use team synchronization for team membership. Groups over 5,000 members are not supported.
- Set the unaffiliated users policy to remove users from the enterprise when they lose their last org membership. (This policy is not available for EMU, which doesn’t need it.)
How (GHES): With SAML and no SCIM, GitHub’s docs say you must manually suspend removed users, or they keep working via tokens and SSH keys. Enable SCIM, or turn on LDAP Sync so disabled directory accounts get suspended on the next run.
Removal deadline: revoke all leavers immediately, including PCI 8.2.5 populations. Complete planned removals at HR’s effective timestamp; contain unplanned/high-risk departures immediately on notification. Daily reconciliation is a backstop, not the SLA.
The IAM owner must implement a trusted HR-to-IdP/SCIM workflow with an on-call fallback, retries, and completion verification. Record HR effective time, notification time, IdP disable, SCIM delivery, GitHub suspension/removal, credential remediation, and negative access tests. Treat any residual access after the deadline as an incident, not a permitted grace period. Review independent app grants and deploy keys that may survive the creator; revoke or reassign them based on scope and risk.
Inactivity deadline: use a daily authoritative inactivity feed and remove/disable inactive human accounts by 60 days under this baseline, never later than 90 days where PCI 8.2.6 applies. The GitHub dormant report is a 30-day candidate list, not a complete inactivity clock: it excludes PAT/SSH/app access and some private Git activity. Correlate IdP and credential/activity telemetry; separately govern non-interactive accounts, emergency accounts, leave arrangements, and any applicable standard exceptions.
IAM must test automated disablement through supported provisioning before production. A downloaded report is not enforcement. Unknown/stale telemetry opens an investigation, never resets inactivity.
Verify:
- Joiner/leaver testing: reconcile all HR leavers with completed removal records daily. Internal audit approves the quarterly sample method, size, and strata, including privileged, contractor, high-risk, and failed-sync cases. Show effective-time comparisons and denied browser/PAT/SSH access, not just an IdP checkbox.
- SCIM health: alert on
external_identity.scim_api_failureandexternal_group.scim_api_failure. A failing sync is a silent offboarding failure. - Dormant accounts: reconcile the candidate report with the daily inactivity feed. Retain disablement decisions and completion events. Test just before, exactly at, and just after the 60-day internal boundary and the applicable 90-day maximum.
- Failure drills: exercise a lost HR event, failed SCIM call, stale feed, and unavailable IdP. Prove escalation and supported emergency containment work without claiming a queued request is completed revocation.
- Mover testing: show that obsolete team membership, direct grants, inherited access, and privileged roles were reviewed together. A successful new assignment does not prove the old access ended.
Least privilege: base permissions and roles
What we do:
- Base permission:
No permissionby default, with explicit team grants. Approved organization-levelReadneeds a deliberate innersource design and the enterprise-policy conditions below. NeverWriteorAdmin. - Org owners: at least two, and as few as possible beyond that.
- Everyone else: predefined or custom roles scoped to the job.
- Security team: the security manager role, not owner.
Why: INT-PCI-7.2 and US-NYDFS-500.7 require documented job-based access. WAF recommends custom roles over owner access, never Admin as a repository default.
Where each tool fits:
| Layer | Tool | FSI default |
|---|---|---|
| Enterprise | Enterprise owner | 2-3 named humans plus break-glass (next section) |
| Enterprise | Enterprise custom roles (public preview; excluded from production) | Use narrowly controlled owner execution and exported evidence for independent reviewers. |
| Enterprise | Enterprise security manager (public preview; excluded from production) | Use the GA organization security manager role in every in-scope organization, reconciled centrally |
| Organization | Security manager (GA) | Assigned to the AppSec team. Read on all repos, write on security alerts. |
| Organization | Custom organization roles | e.g. an auditor role with “View organization audit log” |
| Repository | Custom repository roles | Up to 20; inherit from a base role and add permissions |
| Repository | Base permission | None or Read |
Two facts worth knowing:
- Internal repositories remain enterprise-readable with base permission None. Sensitive repositories must be private.
- Enterprise owners lack org content access by default, but can join any org with an audit event.
Collaboration is not a synonym for broad privilege. Banking and payments engineers can collaborate on an approved shared library while customer-specific applications and restricted infrastructure remain private. Grant contribution rights through scoped teams, retain independent maintainers, and review cross-line access against information barriers.
How: prefer enforced enterprise None with explicit team grants. If organizations choose their own base permission for an approved innersource design, permit only None/Read in the institution standard and reconcile daily. GitHub’s enforced enterprise value is not a configurable ceiling that lets every organization choose a lower value (repository management policies). Restrict outside collaborator invitations to approved owners.
Verify:
Use the organization members API, organization roles API, and outside collaborators API as review inputs. Inspect administrator identities, role definitions and permissions, user/team assignments, external collaborators, and the business approval for each grant. These sources must be joined to direct repository grants, inherited access, and enterprise roles before claiming a complete access review.
Export paginated user and team assignments for every organization role. Add direct grants, role definitions, inheritance, and enterprise roles from the complete review population below. Validate empty results against permissions and inventory.
Audit events to alert on: business.add_admin, org.update_member (role changes), organization_role.assign, enterprise_role.assign, and org.update_default_repository_permission.
Privileged and break-glass accounts
What we do: Day-to-day administration uses named, IdP-provisioned accounts with the narrowest role that works. One break-glass path exists, sealed in your privileged access management (PAM) vault and alarmed.
Why: US-NYDFS-500.7 limits privileged access and its use. Apply the MFA scope and exemptions above to emergency access too. You still need a tested path when the IdP is down, without turning the vault into a self-service admin vending machine.
How:
- EMU: the setup user (
<shortcode>_admin) is your break-glass account. GitHub says it is only for configuring auth and provisioning, SCIM, and regaining access during an IdP problem. Enable GitHub 2FA on it and store credentials in your PAM tool. - All GHEC models: download the SSO recovery codes and seal them alongside it.
- Org ownership: keep at least two owners per org. GitHub’s role guidance says “no less than two.”
- GHES: keep one built-in site admin for when SAML or LDAP breaks, with 2FA, in the vault.
Dual-control checkout: require a linked incident/change record before retrieval, approval by a second authorized person independent of the requester, and PAM enforcement of that approval. Record who requested, approved, retrieved, and used the credential, with start/end times and authorized scope. No self-approval. During an outage, use a preauthorized emergency record and independent approver, then reconcile to ITSM when restored.
Seal password, 2FA material, and recovery codes with access designed for dual control. Alert at checkout and on use. Afterward, rotate/reset affected credentials, replace used recovery material, revoke temporary access, and obtain independent review. Test that one person cannot retrieve the complete emergency credential set.
The setup user’s SCIM token: GitHub requires a classic PAT with no expiration and at least scim:enterprise scope (setup procedure). Record a named exception for this credential, owned by the IAM lead, with quarterly reapproval and rotation at most every 90 days, plus immediate rotation on suspected exposure or custodian change.
Keep only the required scope and store the value in the protected provisioning configuration and approved vault, never a developer workflow. Rotate by issuing a replacement, updating provisioning, testing a joiner/leaver, then revoking the old token. Alert on unexpected provisioning source/volume, setup-user use outside approved configuration, and any token use outside SCIM. Where telemetry cannot attribute every use, document that detection gap and additional monitoring rather than claiming perfect coverage.
Verify: alert on setup-user interactive/configuration activity and distinguish expected, monitored SCIM calls from emergency account use. Correlate credential hashes where emitted with IdP provisioning logs. Test dual approval, retrieval, rotation, and recovery annually; archive checkout and independent post-use review.
Personal access tokens
What we do:
- Restrict classic PATs from accessing enterprise organizations.
- Require approval for fine-grained PATs.
- Set a maximum lifetime of 90 days or less for ordinary PATs, including administrator PATs.
- Prefer GitHub Apps for anything long-lived.
Why: PATs are bearer credentials. Their scope, ownership, issuance, and revocation contribute to access control; PAT lifetime is not MFA and does not by itself satisfy PCI authentication requirements. Prefer fine-grained PATs with independent approval and GitHub’s governance guidance.
How: All three are enterprise policies on one PAT policy page. Facts to plan around:
- The default maximum lifetime for fine-grained tokens is 366 days. Classic tokens have no expiration requirement until you set one.
- Lifetime policy blocks non-compliant tokens; it does not revoke them. Users find out when API calls fail.
- Do not routinely enable “Exempt administrators.” GitHub recommends it for SCIM continuity, but the checkbox exempts administrators as a class, not a single named token. Test provisioning with the proposed lifetime policy before rollout. If the deployment requires that broad switch, escalate a separately approved technical exception: inventory every affected admin credential, enforce the ordinary lifetime independently, reconcile daily, and record the broader enforcement gap. The named SCIM exception alone does not make the UI exemption token-scoped.
- Only fine-grained tokens go through approval. That is why classic tokens must be restricted, not just discouraged.
- On EMU, enterprise PAT policies also cover user namespaces because the enterprise owns the accounts.
The same controls exist on GHES 3.22.
Audit-log API access is not the collection model. Use Part 1’s audit log streaming for continuous delivery. The enterprise audit log REST API currently requires a classic PAT or OAuth token; reserve it for targeted investigation or bounded recovery of an identified gap. Where that access is necessary, independently approve a service-owned credential scoped to read:audit_log, govern its lifetime, and retain the purpose and retrieval scope.
Verify:
personal_access_token.expiration_limit_set,personal_access_token.access_restriction_enabled, andpersonal_access_token.request_created/access_granted/request_deniedevents give you the full approval trail.- Org owners can review and revoke fine-grained tokens in the UI. The REST endpoints (
GET /orgs/{org}/personal-access-tokens) can only be called by a GitHub App. That’s a nice nudge toward building your review tooling as an App. - Reconcile privileged tokens and named exceptions daily. Test that an over-limit ordinary administrator token is denied, provisioning continues with the approved SCIM credential, and revoked replacement tokens cannot work. A policy that blocks use does not delete the credential.
GitHub Apps, OAuth apps, and deploy keys
What we do:
- GitHub Apps are the default for integrations and automation. Approve the supported installation model, explicit permissions, and repository population; broad deployment still needs a business purpose.
- OAuth apps require owner approval and an exception record.
- Deploy keys are disabled at the enterprise level, with documented exceptions.
Why: EU-DORA-RTS-ACCESS includes machine identities. GitHub Apps offer fine-grained permissions, repository scope, and short-lived tokens; WAF prefers them over OAuth.
Deploy keys are a blind spot. Per GitHub’s docs, they never expire, survive the creator’s removal, and with write access act like a repo admin. They also bypass IdP CAP IP conditions.
How:
- OAuth app access restrictions are on by default for new orgs. Keep them on. Note that privileged OAuth apps bypass this.
- Restrict repository admins from installing GitHub Apps so installs route through owner approval.
- Disable deploy keys in enterprise repository policies. Warning: this disables existing keys immediately. Inventory first.
Use the repository inventory API and deploy keys API to establish which repositories have keys, their key IDs, titles, creation dates, and read/write status. Join each key to a service owner, purpose, consumer, and retirement plan before disabling it. Key metadata alone does not establish whether a business process still depends on it.
Reconcile the observed repository count with the approved population. A hidden repository, denied key endpoint, or failed page makes the inventory incomplete. Installed apps and deploy keys require separate inventories; the enterprise credential CSV is not a complete list of every short-lived installation token or deploy key.
Verify: integration_installation.create / destroy, org.oauth_app_access_approved, and public_key.create (fires for both user SSH keys and deploy keys). Review the installed app list and permissions quarterly.
Operate service identities throughout their lifecycle
What: a machine identity with an accountable owner
Every integration has a service record, a primary owner, and backup operational coverage. Record the business purpose, criticality, installation/account IDs, repositories, permissions, credential custody, rotation method, and shutdown procedure.
GitHub Apps, OAuth grants, service-account PATs, deploy keys, and GITHUB_TOKEN have different lifecycles. An app installation is not an employee entitlement, and its access may survive the departure of whoever created it.
Why: keep release operations independent of employee credentials
A release publisher should not fail because its original author changes jobs. Equally, a retired banking application should not retain an integration that can still write to its repositories.
Short-lived installation tokens reduce exposure, but the app’s private key and installation permissions still require custody and review. Prefer the documented GitHub App model where the required API supports it; retain named exceptions where it does not.
How: scope, test, rotate, and retire
| FI use case | Identity pattern | Operating requirement |
|---|---|---|
| Shared-library release publisher | Approved GitHub App with supported release permissions | Limit repositories; protect key custody; separately govern any tag/ruleset bypass |
| Repository inventory collector | GitHub App where supported | Prove complete read scope and alert on missing repositories or permission failures |
| Audit-log API fallback | Exception-owned service identity with the API’s supported classic PAT/OAuth authentication | Prefer streaming; scope the credential, set rotation, and test collection continuity |
| Build access within an Actions job | Job-scoped GITHUB_TOKEN with explicit required permissions |
Apply Part 6’s pipeline controls and verify actual network access |
| Legacy deployment integration | Documented exception if an App cannot meet the need | Name a migration/retirement owner and test expiry, revocation, and failure recovery |
Before installation or scope expansion, the integration owner documents needed operations and repositories. An independent authorized owner approves the grant; security reviews sensitive permissions and provider access according to risk.
Test both a required operation and a forbidden one in an isolated repository. Confirm that the integration cannot reach an unrelated banking-app repository or perform an unneeded write. Endpoint permission requirements vary; choose the smallest tested set, not an invented universal permission bundle.
Keep keys and tokens in approved secrets infrastructure. Document renewal and rotation steps, including how operators verify replacement access before disabling the old credential. Test the failure path without extending a credential indefinitely to avoid an outage.
On owner departure, reassess custody and rotate affected credentials based on exposure and access. On service retirement, disable scheduled consumers, remove installations/grants and vault material through controlled procedures, then verify denial and update the inventory.
Verify: prove ownership and continuity
Review service identities quarterly, with more frequent reconciliation for privileged integrations and named exceptions. Compare approved scope with installed permissions and observed use; investigate unused grants and unexplained expansion.
Rehearse release-publisher key rotation and collector credential failure. Record operational interruption, alert routing, recovery time, and denied old-credential access. For critical services, demonstrate that the backup operator can follow the runbook.
Reviewers need installation, key, and service-account inventories as well as the enterprise credential export. No single report represents every machine-access path.
SSH certificate authorities
What we do: Issue short-lived SSH certificates from a corporate CA and require certificates for SSH Git access. Govern HTTPS/PAT access separately; an SSH certificate requirement does not disable HTTPS.
Why: Daily-expiring certificates limit stolen-key exposure and centralize command-line access. This contributes to INT-PCI-8.3 and US-NYDFS-500.7.
How: the CA verifies MFA and current eligibility, signs a short-lived certificate bound to the GitHub login, and logs issuance. Protect its key in signing infrastructure, not ./ca-key on a laptop. Local signing is not a production access control.
- Add the CA at the enterprise level, then require SSH certificates.
- CAs added before March 27, 2024 can issue non-expiring certs. Upgrade them.
- Certificates work under SAML SSO without per-credential authorization.
- GHES 3.22 supports SSH CAs too (enterprise security policies).
Verify: ssh_certificate_authority.create / destroy and ssh_certificate_requirement.enable / disable events. Your CA’s issuance log is the other half of the evidence.
Network and session controls
Apply Part 1’s network boundary policy. For IAM, coverage follows the credential path, not the authentication protocol. Entra OIDC can use GitHub’s GA IP list; selecting the IdP list deactivates it. Neither network restriction substitutes for MFA.
How:
- GitHub’s allow list covers PATs, OAuth tokens, SSH keys (including deploy keys), and app tokens, including
GITHUB_TOKEN. - With EMU, turn on user-level enforcement so the list also covers user-owned repositories and forks.
- Add your integrations’ ranges, or allow installed GitHub Apps. WAF essentials warns that missing ranges make integrations “silently fail.”
- Test CI credentials from approved fixed egress using Part 6’s runner design. Do not widen the list to standard hosted-runner ranges.
- CAP does not cover deploy keys or installation tokens acting as an app. GitHub’s list also has documented exceptions, including some user-account app installations and EMU provisioning. Inventory and mitigate these paths explicitly.
- On GHES, the instance sits inside your network, so this is a firewall and load balancer job.
Verify: diff enterprise and organization-added entries against the approved network inventory quarterly. Test allowed and denied browser, PAT, SSH, deploy-key, and installation-token paths, including CI’s GITHUB_TOKEN. Retain results and remediate coverage gaps.
Credential revocation and incident response
What we do: Rehearse three moves: revoke one user’s credentials, revoke a credential type, and lock down SSO for everyone.
Why: US-NYDFS-500.14 expects you to detect and respond to unauthorized access. When a laptop is stolen or a token leaks, minutes matter.
How:
| Situation | Action | Source |
|---|---|---|
| Assess exposure | Export the enterprise credential inventory (CSV of PATs, OAuth tokens, App tokens, SSH keys) | Reviewing credentials |
| One compromised user | Revoke SSO authorizations; on EMU, also delete their tokens and SSH keys | Revoking authorizations |
| One credential type | Revoke a type (e.g. all classic PATs) enterprise-wide | Same page |
| Major incident | Lock down SSO for everyone except enterprise owners | Locking down SSO |
| Token found in the wild | Submit it to the unauthenticated revocation API | Credential revocation API |
Use the supported UI or build an authorized runbook from the current credential authorization REST reference. No apply-verbatim destructive command is provided here. The implementation must verify enterprise/model, target identity, credential type, approval, endpoint permissions, and completion.
Revocation endpoints may return 202 Accepted for queued work. That is not completed containment. Check subsequent inventory and deny tests, and use IdP/SCIM suspension immediately where appropriate. Destroying credentials is available for EMU; personal-account SSO revocation does not delete the underlying token. Installation tokens and deploy keys need separate remediation at their management layer.
Verify: The audit log records hashed_token for token-authenticated events. Use the credential inventory export to trace everything a compromised token did. Run a tabletop at least annually and keep the notes.
Access reviews and the evidence pack
What we do: A quarterly access review, owned by the business, with a repeatable evidence pack. Repository owners explain need; IAM supplies effective access and removal evidence; independent reviewers make and track decisions.
Why: US-NYDFS-500.7 and INT-PCI-7.2 require periodic review; the WAF checklist also calls for discrepancy closure. Part 1’s evidence archive is a prerequisite because GitHub’s audit log is not long-term retention.
The quarterly evidence pack:
- Membership export (UI) with 2FA status, roles, and SAML
NameID. - Credential inventory export (UI) showing every active token and key.
- Complete access population, not just privileged names: enterprise/org roles, custom-role definitions and user/team assignments, base permissions, internal-repo exposure, team/nested-team membership, every repository’s team grants and direct collaborator grants, and repository custom roles with effective permissions. Include outside/guest collaborators and pending grants. Use paginated collaborator, team, and custom repository role APIs, supplemented by supported exports.
- Outside or guest collaborators with their sponsor and end date.
- Installed GitHub Apps and approved OAuth apps with permissions.
- Dormant users report.
-
Audit log extract of privileged changes for the review period:
Use the enterprise audit-log API to collect privileged changes over the actual review period. Retain action, actor, affected identity/role, scope, timestamp, and available authorization context. Correlate administrator additions with approved access decisions and subsequent removal where time-bound.
Repeat for
org.update_member,organization_role.assign,enterprise_role.assign, andpersonal_access_token.access_granted. - Independent reviewer sign-off with decisions (keep, remove, change) and closure tickets. Nobody certifies their own access; another authorized business/control owner reviews the reviewer’s entitlements.
Prove completeness before review: discover all enterprise organizations, reconcile every repository ID with the approved inventory, and validate collector permissions. Record collection time, filters, page counts, source counts, failed endpoints, and duplicate handling. Match direct and inherited grants to current HR/IdP identities and app sponsors. Missing or inaccessible populations block sign-off. Internal audit agrees sampling only after population completeness, using documented risk-based selection instead of a fixed ten-person sample.
Verify the review: assign removal owners/deadlines, track overdue decisions, and prove removal with events and denied-access tests. Archive source exports, collection manifests, approvals, and closure records under Part 1’s retention and integrity standard. Controlled, completeness-tested UI exports are valid evidence, not automatically generated evidence.
Control matrix
Every row contributes to the named requirements only. Adequacy and applicability require second-line approval.
| Control | Contributes to requirement IDs | GitHub feature | Evidence | Cadence |
|---|---|---|---|---|
| IdP-owned accounts | EU-DORA-RTS-ACCESS, US-NYDFS-500.7, US-GLBA-314 | Enterprise Managed Users | Enterprise type; IdP vs. membership reconciliation | Once; daily reconciliation; quarterly certification |
| Central auth and conditional access | US-NYDFS-500.12, US-NYDFS-500.14, INT-SWIFT-CSCF-OVERVIEW | OIDC + CAP, or enterprise SAML + IP allow list | SAML/OIDC and ip_allow_list.* audit events; CAP export |
Continuous alerts; quarterly |
| Human MFA and credential-path coverage | US-NYDFS-500.12, INT-PCI-8.3, INT-PCI-8.4 | IdP MFA; GitHub 2FA; governed credential paths | MFA-chain and bypass tests; applicable exemptions/compensating approvals; sign-in evidence | Quarterly; compensating controls at least annually |
| Immediate leaver removal and inactivity control | US-NYDFS-500.7, INT-PCI-7.2, INT-PCI-8.2.5, INT-PCI-8.2.6, EU-DORA-RTS-ACCESS | Trusted HR workflow, SCIM, IdP suspension and reconciliation | Complete leaver timestamps/deny tests; daily inactivity and disablement evidence | Immediate removal; daily reconciliation; quarterly independent sample |
| Least privilege roles | INT-PCI-7.2, US-NYDFS-500.7, US-OCC-30-AppB | Base permissions, custom and security manager roles | Role holder lists; role/permission audit events | Quarterly |
| Break-glass | US-NYDFS-500.7, US-NYDFS-500.12 | PAM dual-control checkout; setup user; recovery codes | Prior incident/change, independent approval, checkout/use/rotation and denied single-person retrieval | Per use; annual drill |
| Token governance | INT-PCI-8.3, EU-DORA-RTS-ACCESS | Classic PAT restriction, fine-grained approval, max lifetime | personal_access_token.* events; credential inventory |
Continuous; quarterly |
| Integration governance | EU-DORA-RTS-ACCESS, US-FFIEC-INFOSEC | GitHub Apps, OAuth restrictions, deploy key policy | integration_installation.*, org.oauth_app_access_*, public_key.create |
Quarterly |
| Short-lived SSH Git credentials | US-NYDFS-500.7, EU-DORA-RTS-ACCESS | Protected SSH CA; HTTPS governed separately | Issuance/MFA/eligibility logs, expiry tests, CA custody evidence | Quarterly |
| Incident revocation | US-NYDFS-500.14, CA-OSFI-B13 | Credential inventory, revocation, SSO lockdown | Runbook; tabletop record; hashed_token traces |
Annual test |
| Periodic access review | US-NYDFS-500.7, INT-PCI-7.2, AR-BCRA-A7724, PE-SBS-504 | Complete exports, paginated REST, audit log and archive | Reconciled direct/inherited/custom-role population, independent sign-off, approved sampling and closure | Quarterly |
Summary and key takeaways
Usable IAM gets the right engineer to useful work and removes access when the need ends. Service identities need the same clarity of purpose, but their own custody, rotation, and retirement paths.
Measure onboarding friction alongside revocation performance. Neither a fast approval queue nor a clean quarterly spreadsheet proves that access works as designed.
Your checklist:
- Publish job-based access bundles, approvers, setup instructions, and escalation routes for engineers, vendors, and release operators.
- Test one joiner and one business-line move through a real, low-risk contribution. Remove obsolete access and time-box any approved overlap.
- Implement the approved account model; personal-account enterprises enforce enterprise SAML.
- Prefer Entra OIDC authentication where appropriate; default to the GA GitHub IP list and test every credential/CI path.
- Enforce phishing-resistant MFA at the IdP; on personal accounts, also require GitHub 2FA with secure methods.
- Implement immediate leaver removal, daily completion checks, and automated inactivity disablement with tested failure paths.
- Default to enforced base permission None and scoped teams; govern any Read alternative. Keep owner counts minimal but at least two per organization, and use suitable custom/security manager roles.
- Require independently approved PAM checkout before emergency retrieval. Rotate the named SCIM exception within 90 days; do not silently exempt all admins.
- Restrict classic PATs, require fine-grained PAT approval, and set a maximum lifetime.
- Default to GitHub Apps; keep OAuth restrictions on; disable deploy keys after inventory.
- Give every service identity a business purpose, owner, backup operator, tested permission scope, rotation runbook, and retirement path.
- Stand up an SSH CA with short-lived certificates and require it.
- Script and rehearse credential revocation and SSO lockdown.
- Review the complete direct/inherited/custom-role population independently, reconcile counts, and archive evidence and timely closure.
Start here: follow one banking engineer’s move between teams and one release integration’s credential rotation. Fix the points where ownership, access, or removal cannot be demonstrated.
Previous: Part 1: Platform Foundation and Data Boundaries
Next: Part 3: Source Code Management and Change Control, where clear ownership and scoped access support discoverable repositories, useful reviews, and controlled contributions across teams.
References
Regulatory sources are linked in The requirements; procedures are linked beside each control. Further reading:
- Essentials of governance and administration with GitHub Enterprise
- Securing the developer workspace
- Restricting network traffic to your enterprise with an IP allow list
- About the audit log for your enterprise
- Audit log events for your enterprise
- Larger-runner capabilities (implementation owned by Part 6)
- GitHub Pre-release License Terms
Comments