SSO with GitHub
Use GitHub organization membership for authentication with the UptimeHunt platform GitHub App.
SSO with GitHub
Overview
UptimeHunt uses the platform GitHub App ("Continue with GitHub") for GitHub-based sign-in. Members of your GitHub organization sign in with the platform-wide GitHub button — no per-organization OAuth App to register, no client ID or secret to copy.
Membership is verified in real time through the UptimeHunt GitHub App installed on your GitHub organization. All GitHub settings live under Organization → Workspace settings → Authentication.
GitHub differs from corporate IdPs: it asserts the user's own (often personal) email addresses, not addresses under a domain you control. GitHub sign-in is therefore never email-routed — entering an email on the login page does not offer a GitHub route until an account already exists — and domain verification plays no role for GitHub.
Deployment requirement
GitHub sign-in requires the UptimeHunt platform GitHub App to be configured at the deployment level. The UI indicates when it is unavailable.
Setup
Step 1 — Select GitHub as the sign-in provider
Go to Organization → Workspace settings → Authentication. In the provider dropdown, select GitHub. The GitHub settings card appears below.
Step 2 — Enter your GitHub organization name
UI field: GitHub organization
Hint: "Only ACTIVE members of this GitHub organization may join. Requires the platform GitHub App installed there; membership is verified through the App installation. Leave empty to disable mapping."
Enter your GitHub organization's login name — the slug from github.com/{your-org}. For example, for github.com/acme-engineering enter acme-engineering.
Setting this field opts the organization into platform GitHub membership mapping; clearing it switches mapping off.
API:
curl -X PATCH https://app.uptimehunt.io/api/v1/orgs/acme \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"github_org": "acme", "github_default_role": "member", "github_auto_join": false}'Step 3 — Install the UptimeHunt GitHub App
A GitHub organization owner installs the UptimeHunt GitHub App on your GitHub organization (GitHub → your organization → Settings → GitHub Apps) — a one-time, explicit approval. The App asks only for the Organization members: read permission, which is how UptimeHunt verifies membership.
Why a GitHub App?
OAuth tokens are subject to GitHub's OAuth app access restrictions (on by default for new GitHub organizations): an app cannot see a user's membership of a restricted organization until that org's admin approves the app — an invisible failure mode. A GitHub App installation is a single explicit grant by the org admin, after which UptimeHunt checks membership with the installation token, unaffected by per-user access restrictions. Membership checks fail closed: if GitHub cannot confirm an active membership, access is not granted.
Step 4 — Check App installation
After entering the GitHub organization name, the Check App installation button appears. Click it to verify the UptimeHunt GitHub App is installed on your GitHub organization before the first save.
The button calls the SSO configuration test (POST /api/v1/orgs/{slug}/sso/test) and evaluates the github_app_installed check — it confirms the App is installed on the named GitHub organization. The result appears inline:
- A green status indicates the App is installed and membership verification is working.
- A yellow warning gives the specific problem (not installed, or platform App not configured on this deployment).
API equivalent:
curl -X POST https://app.uptimehunt.io/api/v1/orgs/acme/sso/test \
-H "Authorization: Bearer {access_token}"The response's checks array includes {"name": "github_app_installed", "ok": true|false, "detail": "..."}.
Step 5 — Configure the toggles
Auto-join verified members (checkbox)
Hint: "On: verified GitHub-organization members join automatically when they sign in with GitHub. Off: sign-in only offers this organization — members confirm the join explicitly."
- Off (default): eligible organizations appear under "Available via GitHub" in the organization switcher and in a post-sign-in card. The member clicks Join to enroll explicitly.
- On: the member is added automatically at sign-in. Still verified against GitHub in real time and counted against the org's member limit.
Default role for GitHub-mapped members (select)
Hint: "Granted to verified members matched by no team-role rule below — overridden by the team-role mapping and its fall-through policy."
Options: Member (default), Admin. Cannot be set to Owner here; use the Team-role mapping to grant Owner to a specific GitHub team.
Click Save GitHub settings to persist all settings.
How members get in
- The member signs in with the platform-wide Sign in with GitHub button.
- After sign-in the platform checks GitHub membership in real time for every organization that has named a GitHub organization.
- If auto-join is off (the default): eligible organizations appear under "Available via GitHub"; the member clicks Join to enroll.
- If auto-join is on: the member is added automatically.
- Every join (explicit or auto) is verified against GitHub in real time and recorded in the audit log (
member.joined).
API reference:
| Endpoint | Purpose |
|---|---|
GET /api/v1/auth/github/orgs | The signed-in GitHub account's mapped organizations with status (member, eligible, dedicated_account_required) |
POST /api/v1/auth/github/orgs/{slug}/join | Explicit join (201; idempotent) |
GET /api/v1/auth/github/orgs status values:
status | Meaning |
|---|---|
member | Already a member of this UptimeHunt organization |
eligible | Verified active GitHub-org member; can join |
dedicated_account_required | The organization has require_dedicated_accounts enabled — the platform-GitHub path refuses mapping. The member must sign in through the organization's own IdP instead |
Join error codes:
| Code | HTTP | Meaning |
|---|---|---|
github_realm_required | 403 | The caller's account is not a platform GitHub (social:github) realm account — only accounts signed in via the platform "Continue with GitHub" button can use mapped-org join |
github_app_not_configured | 403 | The platform GitHub App is not configured at the deployment level — GitHub membership mapping is unavailable on this instance |
not_found | 404 | The organization slug does not exist, or the organization has no GitHub mapping configured (github_org is empty) — unknown orgs and unmapped orgs answer identically (no mapping-existence oracle) |
dedicated_account_required | 403 | The organization requires dedicated accounts — the platform-GitHub path is not permitted; use the organization's own sign-in |
github_app_not_installed | 409 | The platform GitHub App is not installed on the GitHub organization |
github_unavailable | 502 | GitHub API is unreachable |
github_org_mismatch | 403 | Not an active member of the configured GitHub organization |
team_role_denied | 403 | A GitHub team-role rule matches the member's teams and the result denies access to this organization |
team_lookup_failed | 403 | GitHub's teams API could not be reached to verify team membership, and the organization's unmapped-members policy is Refuse sign-in — a new mapping cannot be created while teams are unverifiable |
member_cap_reached | 403 | The organization's member limit has been reached |
One account, many organizations
The same GitHub identity holds ordinary memberships in every organization that has configured GitHub mapping. Removing the member from the GitHub organization stops new joins immediately; their existing UptimeHunt membership is removed through normal offboarding.
Team-Role Mapping
The Team-role mapping section (Organization → Workspace settings → Authentication, below the GitHub organization settings) maps a member's GitHub team membership to a UptimeHunt role. It is an org-level mapping evaluated at every sign-in.
Configuring rules
The editor shows an ordered list of rules. Each row pairs a Team slug (the slug from the team's GitHub URL: github.com/orgs/acme/teams/platform-leads → platform-leads) with a role. Use the move up/move down controls to reorder.
- First match wins. Rules are evaluated top-down; the first team the member belongs to decides their role — order rules most-privileged first.
- Team slug constraints: max 39 characters, only letters, digits, hyphens and underscores (
^[A-Za-z0-9_-]+$). A duplicate team slug is rejected (it could never win under first-match). - Up to 50 rules.
- Owner is permitted but warned. The editor shows a warning when a rule grants Owner. The Default role for GitHub-mapped members select (above) cannot be set to Owner; only an explicit team rule can grant it.
Members matched by no rule (select):
| UI label | Effect |
|---|---|
| Use default role (default) | Fall back to Default role for GitHub-mapped members |
| Member | Grant the Member role |
| Admin | Grant the Admin role |
| Refuse sign-in | Deny the organization entirely — the member cannot join |
Refuse sign-in is strict
With Refuse sign-in selected, a verified GitHub member who is in none of the mapped teams is denied access. New just-in-time sign-ins are also refused when the teams API cannot be read (lookup failure) — a brand-new account cannot be provisioned while team membership is unverifiable.
Click Save GitHub settings after configuring the rules.
Re-sync, the last-Owner guard, and lookup failures
- Re-synced on every sign-in. The mapping is re-evaluated each time a member signs in — moving someone between GitHub teams updates their UptimeHunt role at their next sign-in. A role change emits
github.team_role.applied. - Last-Owner guard. The organization's sole Owner is never locked out by team mapping. A mapping result that would demote the only Owner is skipped; a Refuse sign-in result for the only Owner does not refuse the sign-in — they keep their role and sign in normally.
- Lookup failures fail safe, asymmetrically. If GitHub's teams API cannot be read at sign-in:
- Existing members keep their current role — the sign-in continues and
github.team_role.lookup_failedis recorded. - New JIT sign-ins are refused when the unmapped-members policy is Refuse sign-in. Under any other policy, the new member is provisioned with the default role and the fallback is recorded.
- Existing members keep their current role — the sign-in continues and
How teams are looked up
Each rule's team is checked with the platform GitHub App installation token (GET /orgs/{github_org}/teams/{team_slug}/memberships/{username}) — this is why the App needs Organization members: read.
Example
For a github_org of acme, ordered most-privileged-first:
| # | Team slug | Role |
|---|---|---|
| 1 | platform-leads | Owner |
| 2 | sre | Admin |
| 3 | engineering | Member |
With Members matched by no rule set to Refuse sign-in: a member of both sre and engineering becomes an Admin (rule 2 wins before rule 3); a member of only platform-leads becomes an Owner; a verified GitHub member in none of the three teams is denied access.
API
curl -X PATCH https://app.uptimehunt.io/api/v1/orgs/acme \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{
"github_team_role_rules": [
{"team_slug": "platform-leads", "role": "owner"},
{"team_slug": "sre", "role": "admin"},
{"team_slug": "engineering", "role": "member"}
],
"github_team_roles_unmapped_policy": "deny"
}'| Field | Description |
|---|---|
github_team_role_rules | Ordered list of {"team_slug", "role"} objects — first match wins. Empty list = mapping off. At most 50; duplicate team_slug rejected |
github_team_roles_unmapped_policy | '' (Use default role), member, admin, or deny (Refuse sign-in). Only consulted while github_team_role_rules is non-empty |
Audit events
| Event | When |
|---|---|
github.team_mapping.updated | The rule list or the unmapped-members policy was changed |
github.team_role.applied | A sign-in re-synced a member's role from their team membership; team_slug is the matched rule's team slug (or null when the unmapped policy decided) |
github.team_role.denied | A Refuse sign-in policy refused the join/sign-in |
github.team_role.lookup_failed | GitHub's teams API could not be read at sign-in; detail depends on whether this is an existing member (keeps role), a new JIT under Refuse sign-in (refused), or a new JIT under another policy (provisioned with default role) |
Troubleshooting
| Symptom | Likely cause |
|---|---|
| An eligible member doesn't see the organization after GitHub sign-in | The UptimeHunt GitHub App is not installed on the GitHub organization (click Check App installation in the Authentication tab), the member's GitHub membership is not active (pending invitation), or they signed in with a different login method (password/Google) |
github_app_not_installed (409) on join | Install the UptimeHunt GitHub App on the GitHub organization (step 3) |
| Check App installation button returns a warning | The platform GitHub App is not configured on this deployment — GitHub SSO cannot be enabled here |
Sign-in rejected with github_org_mismatch | The GitHub account is not an active member of the configured GitHub organization (a pending GitHub invitation doesn't count) |
| The GitHub organization field and Save GitHub settings are greyed out | The platform GitHub App is not configured on this deployment |
| Login page never offers a GitHub route after entering an email | Expected until that user's GitHub-linked account exists — GitHub is not email-routed. Use the Use single sign-on option, or sign in with the platform GitHub button |
| A member got the wrong role from Team-role mapping | Rules are first-match, ordered top-down — a more-privileged rule earlier in the list wins. Reorder with move up/move down. The role re-syncs at the member's next sign-in, not retroactively |
| A member in the right team is refused sign-in | With Refuse sign-in selected and the member in no mapped team — confirm the GitHub team slug exactly matches the URL slug, not the display name. For new accounts, a GitHub teams API failure also refuses provisioning under this policy |
| The sole Owner's role didn't change after moving GitHub teams | The last-Owner guard refuses any mapping result that would demote the only Owner; promote another member to Owner first |