Integration
Temporary GitHub organization access
Also searched as temporary GitHub team membership, time-limited GitHub org access, and automatically removing someone from a GitHub organization.
Of the platforms worth governing, GitHub is the one with no answer of its own. That turns out to be the clearest case for putting the expiry somewhere else.
GitHub has no time-bound team membership. A user is on a team or is not, and nothing in the membership records when it should end. Nothing in GitHub — not teams, not organization roles, not repository permissions — takes a duration, so every expiring GitHub access you have ever seen was enforced by something outside GitHub.
The two things that look like an answer and are not
Invitations expire. Membership does not.
An organization invitation that is not accepted expires after seven days, which is a real expiry and is why "GitHub access expires" turns up in search results. It is an expiry on the offer. The moment somebody accepts, the seven days are spent and the membership that results has no end date at all. Worth knowing for a different reason too: an invitation generated by a SCIM request from your identity provider does not expire on that schedule.
Team synchronisation moves membership. It does not time it.
GitHub Enterprise Cloud can synchronize team membership from an identity provider group, which is genuinely useful and is the right way to run standing membership. It has no time dimension of its own — it mirrors the IdP group, so whatever governs that group governs GitHub. If the IdP group has no expiry either, synchronising it faithfully reproduces that. The clock has to exist somewhere, and team sync is not where.
So the expiry lives outside GitHub. Where?
In practice, one of three places, and they are the same three you end up with on every platform that offers nothing:
- A calendar reminder. Honest about what it is, and it works until the week somebody is on leave. It is also invisible: nothing records that the access was supposed to end, so nobody auditing later can tell an expired grant from one that was never meant to expire.
- A scheduled GitHub Action or script. Correct in shape, and now a service you operate. The failure mode is the one every home-built expiry engine has: removal is the unattended half, so when the job stops, grants keep working and nothing is removed. There is no alert for "the thing that was supposed to happen did not", unless you wrote one.
- A tool that owns the grant and the expiry together. The rest of this page.
The credential decision, which matters more than it looks
Whatever removes members has to authenticate, and there are two shapes of GitHub credential. The difference is not convenience.
A personal access token belongs to a person. It carries what that person can do, its actions are attributed to them, and it dies with their account. That last clause is the problem: the day the admin who set it up leaves, offboarding revokes your access management, and nobody connects the two events. An OAuth App does not help — it only ever obtains user-to-server tokens, so its calls are still a human's.
A GitHub App installation is the only GitHub credential owned by the organization. Its permissions are declared by the app and granted at install time, its calls are attributed to the app, and no departure revokes it. If you build the scheduled-job version, build it on an App. It is more work at setup and it is the difference between a control that survives staff turnover and one that does not.
GitHub's REST API requires a User-Agent header. Without one it answers
403 "Request forbidden by administrative rules" — which reads exactly
like a scope or permission failure and sends people to re-issue credentials that were
fine. If a working token starts failing with that message, check the header before the
permissions.
How TemprBac does it
TemprBac holds the expiry itself and calls GitHub's REST API to add and remove team members, so GitHub gets time-bound membership without GitHub needing to support it — and the same package can carry a GitHub team alongside groups in Entra ID, Okta, Google Workspace, AWS IAM and on-premises Active Directory, under one end time.
The GitHub-specific details:
- Teams are addressed by slug (
platform-engineers), not by display name, and people by GitHub username — GitHub's team membership API does not accept email addresses, which is the first thing that surprises anyone wiring this up from a directory where email is the key. - A GitHub App installation is the recommended credential, for the reason above; a personal access token works and is documented as the quicker path for a test organization. The App needs the Members: read & write organization permission.
- Both list calls are fully paginated. A roster that stops at 100 looks complete and silently drops everyone after it — which matters when a team is used to name approvers rather than just to hold access.
- Expiry is checked on a ten-second cycle and removal is a direct API call, not a directory sync, so there is no provisioning window between the grant ending and the access going away.
- Nothing may block revocation. No plan limit, quota or billing state stops access from expiring — an invariant rather than a feature, because a cap able to stop revocation would turn every temporary grant into a permanent one.
TemprBac grants and revokes organization team membership. It does not manage outside collaborators added directly to a repository, repository-level permissions set per user rather than per team, or anything about the code — and it is not your identity provider, so GitHub sign-in and whatever SSO you enforce on it are unchanged. GitHub also does not return email addresses in team member listings unless the credential has elevated scope, so a member's address may simply be blank in a roster view.