Plugin Store · Identity provider

GitHub

Docs GitHub
v1.0.2

Developers sign in with GitHub for a budgeted key.

What it is A GitHub sign-in becomes the way developers self-serve a busbar key.
When to use it Your org already lives on GitHub and org membership should decide team + budget.
CategoryIdentity provider StatusAvailable

Overview

The GitHub auth method turns a GitHub sign-in into the way developers get a busbar key: busbar hosts token-exchange itself, so a dev signs in with GitHub and gets back their own key. A dev opens GET /auth/token, signs in with GitHub, and busbar hands back their own static, self-scoped key, its budget auto-provisioned from the team’s child_default on first exchange, the secret shown on sign-in and again any time you sign in.

GitHub is OAuth, not OIDC: it issues an opaque access token and no id_token JWT, so there is nothing to verify offline against a JWKS. Identity comes from the GitHub REST API instead: after the OAuth code exchange, busbar reads GET /user for the account (identity of record is the GitHub logingithub:<login>) and GET /user/orgs for org membership, which becomes github:org/<org> groups. role_bindings.github then maps those org groups to a busbar team, and therefore to that team’s budget and pools.

The GitHub OAuth App’s confidential client secret is busbar’s alone: it lives in the method’s browser_login.client_secret and busbar injects it only into the token-exchange step; the plugin writes the key, never the value. The login uses the authorization-code flow with PKCE (S256).

It’s one key per person, not a key list: the key is deterministic, derived from the signed-in identity under auth.signing_key. Re-login just shows a dev their same key again, next to a Refresh button that rotates it (the old key dies, the new one takes its place). How long an issued key stays valid is auth.key_ttl (default 90d): an admin-set config value, never a per-dev choice. See How token-exchange works for the shared token system behind this.

Define the provider under identity-providers: with module: github (the map key is the provider instance name, which role_bindings and auth.chain reference it by) and busbar exposes the exchange automatically. Its browser_login block is the switch for the hosted sign-in button; because it carries the OAuth client secret, browser login for GitHub always needs it. The button’s label and GitHub icon are inferred; nothing to set. Define a second provider (e.g. an SSO issuer) with its own browser_login and the login page becomes a chooser.

For GitHub Enterprise Server, point the three base URLs at your instance: api_base: https://<host>/api/v3, authorize_base: https://<host>, and token_base: https://<host>. Everything else is identical.

Install it

The GitHub provider ships as a signed kind: auth plugin. Let busbar auto-download it on boot: add it to plugins.fetch (verified by sha256 + ed25519 signature + allowlist before load), set your public_url, and define the provider:

public_url: "https://busbar.example.com"   # busbar builds /auth/token itself
plugins:
  enabled: true
  fetch:
    - github: GetBusbar/[email protected]     # auto-downloaded, version-pinned
identity-providers:
  github:                                      # map key == module name: must be "github"
    module: github
    settings:
      client_id: "<github-oauth-client-id>"
      scopes: [read:org, read:user]           # defaults; read:org powers the org → groups hop
    browser_login:                            # present → "Continue with GitHub" button
      client_secret: { env: GITHUB_OAUTH_SECRET }
auth:
  signing_key: { file: /run/secrets/busbar-signing.key }
  chain: [keys]                               # devs' issued keys authenticate here
  key_ttl: 90d                                # admin-set; default 90d, no dev choice
  role_bindings:
    github:
      "github:org/<org>": { group: engineering }

New in busbar 1.5.2 (token-exchange). The plugin is independently versioned from busbar itself; pin both explicitly in production (the @1.0.0 in fetch is the pin).

Prefer to install by hand? No plugins.fetch config needed: download the signed tarball (links above), drop it in your plugins.dir, and busbar loads it at boot; signature and trust checks still apply. You can also push it live over the admin API. See the plugin install docs.

Settings

SettingRequiredDefaultDescription
client_id Yes The GitHub OAuth App (or GitHub App) client id. Presented on the authorize URL and the token exchange. The matching client secret is never here; it’s browser_login.client_secret, held by busbar.
scopes No [read:org, read:user] read:org lets the /user/orgs hop enumerate org memberships into github:org/<org> groups; read:user reads the /user profile. Request-time extras are folded in and de-duplicated.
api_base No https://api.github.com REST API base (/user, /user/orgs). For GitHub Enterprise Server set https://<host>/api/v3.
authorize_base No https://github.com Web base for the authorize endpoint (/login/oauth/authorize). For GHES set https://<host>.
token_base No https://github.com Web base for the token endpoint (/login/oauth/access_token). For GHES set https://<host>.
fetch_orgs No true Chain the /user/orgs hop and populate github:org/<org> groups. Set false to establish identity from /user alone (no groups, one fewer hop).
ca_cert_pem No Additional trusted root CA (PEM) for a GHES instance behind an internal CA. Accepted for forward-compatibility; on the committed 1.5.2 ABI the core executes every hop, so this is captured but not yet delivered to the hop executor.
browser_login No Presence is the switch for the hosted sign-in button on GET /auth/token. Because GitHub is a redirect/OAuth method, browser login requires client_secret inside it.
browser_login.client_secret Yes Required whenever browser_login is set: the GitHub OAuth App’s confidential client secret, as a secret reference ({ env: } / { file: }), never a plaintext literal. busbar injects it only into the token-exchange step.

Configuration scenarios

When to use: A developer with just a browser should be able to get a key by signing in with GitHub. busbar hosts the sign-in and hands back the dev’s own budgeted key, ideal for Cursor / Claude Code / VSCode BYOK, where the tool just needs a static key and a base URL.

public_url: "https://busbar.example.com"   # busbar builds /auth/token itself
plugins:
  enabled: true
  fetch:
    - github: GetBusbar/[email protected]
identity-providers:
  github:
    module: github
    settings:
      client_id: "<github-oauth-client-id>"
    browser_login:                            # presence = "Continue with GitHub" button
      client_secret: { env: GITHUB_OAUTH_SECRET }
auth:
  signing_key: { file: /run/secrets/busbar-signing.key }
  chain: [keys]                               # the issued key authenticates here
  key_ttl: 90d                                # admin-set; default 90d, no dev choice
  role_bindings:
    github:
      "github:org/<org>": { group: engineering }

A dev opens GET /auth/token (busbar builds the URL from public_url), clicks Continue with GitHub, authorizes, and busbar returns their own static key: self-scoped github:<login>, budget auto-provisioned from the team’s child_default on first login, the secret shown then and re-shown on every later sign-in. The button label + GitHub icon are inferred; there’s nothing to name.

When to use: Access should follow GitHub org membership: everyone in your GitHub org gets the engineering team’s budget and pools, no per-dev setup.

public_url: "https://busbar.example.com"
plugins:
  enabled: true
  fetch:
    - github: GetBusbar/[email protected]
identity-providers:
  github:
    module: github
    settings:
      client_id: "<github-oauth-client-id>"
      scopes: [read:org, read:user]           # read:org is what powers the org hop
    browser_login:
      client_secret: { env: GITHUB_OAUTH_SECRET }
auth:
  signing_key: { file: /run/secrets/busbar-signing.key }
  chain: [keys]
  key_ttl: 90d
  role_bindings:
    github:
      "github:org/acme":     { group: engineering }
      "github:org/acme-ops": { group: platform }

After sign-in busbar reads GET /user/orgs and emits one github:org/<org> group per org the dev belongs to; role_bindings.github maps each to a busbar team. Note: the shipped plugin emits org groups only (github:org/<org>); it does not fetch or emit per-team (github:team/<org>/<team>) groups, so bind on orgs. Set fetch_orgs: false to skip this hop entirely (identity from /user alone, no groups).

When to use: Your developers live on a self-hosted GitHub Enterprise Server, not github.com. Point the three base URLs at your instance; the token-exchange + identity flow is otherwise unchanged.

public_url: "https://busbar.example.com"
plugins:
  enabled: true
  fetch:
    - github: GetBusbar/[email protected]
identity-providers:
  github:
    module: github
    settings:
      client_id: "<ghes-oauth-client-id>"
      api_base:       "https://ghe.corp.example/api/v3"   # REST API base (note /api/v3)
      authorize_base: "https://ghe.corp.example"          # /login/oauth/authorize
      token_base:     "https://ghe.corp.example"          # /login/oauth/access_token
      ca_cert_pem:    { file: /etc/busbar/ghes-ca.pem }   # if GHES is behind an internal CA
    browser_login:
      client_secret: { env: GITHUB_OAUTH_SECRET }
auth:
  signing_key: { file: /run/secrets/busbar-signing.key }
  chain: [keys]
  key_ttl: 90d
  role_bindings:
    github:
      "github:org/<org>": { group: engineering }

The three *_base overrides are the whole change: api_base gets GHES’s /api/v3 REST root, authorize_base/token_base get its web host. Note: ca_cert_pem is accepted for forward-compatibility but on the committed 1.5.2 ABI the core executes the hops and has no channel to receive it yet, so a GHES behind a private CA needs that CA trusted at the busbar host level for now.

When to use: You just want a GitHub sign-in to mint a key and don’t map org membership to teams, e.g. a small team, a personal instance, or where every dev should land on one default team. Skip the org hop entirely.

public_url: "https://busbar.example.com"
plugins:
  enabled: true
  fetch:
    - github: GetBusbar/[email protected]
identity-providers:
  github:
    module: github
    settings:
      client_id: "<github-oauth-client-id>"
      scopes: [read:user]                      # drop read:org, no org enumeration
      fetch_orgs: false                        # skip the /user/orgs hop; no groups emitted
    browser_login:
      client_secret: { env: GITHUB_OAUTH_SECRET }
auth:
  signing_key: { file: /run/secrets/busbar-signing.key }
  chain: [keys]
  key_ttl: 90d
  # no role_bindings.github needed: with no groups, every dev lands on the default team

fetch_orgs: false establishes identity from GET /user alone (one fewer hop) and emits no github:org/<org> groups, so there is nothing for role_bindings.github to match and every signed-in dev gets the default team’s budget. Dropping read:org from scopes narrows what busbar asks GitHub for, since the org read is no longer needed. Identity is still github:<login>.

← Back to the Plugin Store