Skip to content
Author with AI

<GitHubAuth>

The <GitHubAuth> block authenticates the user to GitHub.

It detects whether the user is already authenticated to GitHub by checking their environment variables and the GitHub CLI. Users can also authenticate manually with OAuth or a Personal Access Token (PAT). Once authenticated, the credentials are available to later Command, Check, and GitClone blocks.

Scripts in a runbook without this block still see a GITHUB_TOKEN set in the environment, but the block is the recommended way to authenticate.

<GitHubAuth
id="gh-auth"
title="Authenticate to GitHub"
description="Sign in to access private repositories"
/>
<GitClone
id="clone-repo"
githubAuthId="gh-auth"
title="Clone Repository"
prefilledUrl="https://github.com/acme-corp/infrastructure-live"
/>

By default, Runbooks detects and uses credentials from:

  1. Environment variables: GITHUB_TOKEN or GH_TOKEN, and GH_ENTERPRISE_TOKEN or GITHUB_ENTERPRISE_TOKEN for a GitHub Enterprise Server host. See Environment variables and hosts.
  2. The GitHub CLI (gh auth token --hostname <host>). When gh isn’t installed, Runbooks reads the token from gh’s hosts.yml instead, unless gh keeps it in the system keyring.

If no credentials are detected, users authenticate manually with OAuth or a PAT.

Method Description
Environment Variable Detected from GITHUB_TOKEN or GH_TOKEN
GitHub CLI Detected from gh auth token
OAuth Sign in via GitHub’s device authorization flow (recommended)
Personal Access Token Enter a PAT (classic or fine-grained) directly in the UI

The GitHubAuth block works with three kinds of GitHub host:

Host kind Example host REST API base URL
GitHub.com (default) github.com https://api.github.com
GitHub Enterprise Cloud with data residency (GHE.com) acme.ghe.com https://api.acme.ghe.com
GitHub Enterprise Server (GHES, self-hosted) github.example.com https://github.example.com/api/v3

Any host that is not github.com or a *.ghe.com tenant is treated as GitHub Enterprise Server. GitHub is always reached over HTTPS, and the OAuth device flow always uses the host’s web origin (https://<host>/login/device). GitHub Enterprise Cloud without data residency lives on github.com, so it needs no host configuration.

Set the host prop to pin the block to one GitHub host. The host picker is hidden, and detection, validation, OAuth and every API call target that host only:

{/* GitHub Enterprise Server */}
<GitHubAuth
id="ghes-auth"
title="Authenticate to GitHub Enterprise"
host="github.example.com"
/>
{/* GitHub Enterprise Cloud with data residency */}
<GitHubAuth
id="ghe-auth"
title="Authenticate to Acme's GitHub"
host="acme.ghe.com"
/>

host accepts a bare hostname, optionally with a port, or a URL such as https://github.example.com, of which only the host part is kept. An API origin is mapped back to its web host, so api.github.com becomes github.com and api.acme.ghe.com becomes acme.ghe.com. A host value that cannot be parsed is reported as a configuration error. The block never falls back to github.com.

Without a host prop, the block offers a GitHub host picker listing every GitHub host Runbooks knows about:

  • Hosts you are logged into with the GitHub CLI (from gh’s hosts.yml, which gh auth login --hostname <host> writes to)
  • The host named by the GH_HOST environment variable
  • The host the current session is already authenticated to
  • Enterprise hosts you recently authenticated to in Runbooks
  • github.com, always

The picker only appears when more than one host is available. The initially selected host is, in order of precedence:

  1. The host the user last picked, if a credential for it is still available
  2. The host named by GH_HOST
  3. github.com

To make an enterprise host show up in the picker, log in to it once with the GitHub CLI:

Terminal window
gh auth login --hostname github.example.com

Runbooks follows the GitHub CLI’s environment variable conventions, but binds every token to one host and never sends it to any other:

Variable Used for
GITHUB_TOKEN, GH_TOKEN The host named by GH_HOST when it is github.com or a *.ghe.com tenant, and github.com otherwise
GH_ENTERPRISE_TOKEN, GITHUB_ENTERPRISE_TOKEN Only the GitHub Enterprise Server host named by GH_HOST. Not used otherwise

For example:

Environment github.com acme.ghe.com github.example.com (GHES)
GITHUB_TOKEN set, no GH_HOST GITHUB_TOKEN - -
GITHUB_TOKEN set, GH_HOST=acme.ghe.com - GITHUB_TOKEN -
GITHUB_TOKEN and GH_ENTERPRISE_TOKEN set, GH_HOST=github.example.com GITHUB_TOKEN - GH_ENTERPRISE_TOKEN
GH_ENTERPRISE_TOKEN set, no GH_HOST - - -

When both variables of a pair are set, GITHUB_TOKEN wins over GH_TOKEN, and GH_ENTERPRISE_TOKEN wins over GITHUB_ENTERPRISE_TOKEN. A GH_HOST that cannot be parsed binds no environment token. For any other host, use the GitHub CLI, OAuth, or a personal access token.

GitHub CLI detection runs gh auth token --hostname <host> for the selected host, so it returns the token gh stored for that host regardless of gh’s own default host.

The built-in Gruntwork Runbooks OAuth app only exists on github.com. To offer Sign in with GitHub on an enterprise host, register an OAuth app on that instance or tenant with device flow enabled, and pass its client ID in oauthClientId.

oauthClientId accepts a string or a map of host to client ID. A string applies to the block’s host when one is set, otherwise to github.com only. Use a map when the host picker is shown and users may pick different hosts.

{/* Pinned host: the string applies to github.example.com */}
<GitHubAuth
id="ghes-auth"
host="github.example.com"
oauthClientId="0a1b2c3d4e5f6a7b8c9d"
/>
{/* Host picker: one OAuth app per host */}
<GitHubAuth
id="gh-auth"
oauthClientId={{
"github.example.com": "0a1b2c3d4e5f6a7b8c9d",
"acme.ghe.com": "9f8e7d6c5b4a39281706",
}}
/>

github.com keeps using the Gruntwork app unless the map, or a string without host, overrides it. For an enterprise host with no client ID, the OAuth tab is disabled with an explanation, and users can still sign in with a personal access token or gh auth login --hostname <host>. With detectCredentials={false} the block ignores the gh login, so only the personal access token is offered. Runbooks never sends the github.com client ID to an enterprise host.

Prop Type Default Description
id string required Unique identifier for this component
title string “GitHub Authentication” Display title shown in the UI
description string - Description of the authentication purpose
autoCollapse boolean false Folds the block down to its title once it’s signed in. See Folding done blocks
inputsId string or string[] - ID of one or more Inputs blocks. title and description resolve {{ .inputs.VarName }} expressions against them
detectCredentials false or CredentialSource[] ['env', 'cli'] Whether and how the block detects credentials in the user’s environment
host string - Pins the GitHub host (github.com, a GHE.com tenant such as acme.ghe.com, or a GitHub Enterprise Server host such as github.example.com) and hides the host picker. See GitHub Enterprise support
oauthClientId string or Record<string, string> Gruntwork default (github.com only) Custom OAuth App client ID. A string applies to host, or to github.com when host is unset. A map is keyed by host. Required for OAuth on enterprise hosts
oauthScopes string[] ["repo"] OAuth scopes to request
defaultTab 'oauth' or 'pat' 'oauth' Which authentication tab the block opens on

When authentication succeeds, these environment variables are set in the session:

Variable Description
GITHUB_TOKEN The GitHub access token
GITHUB_USER The authenticated user’s login name
GITHUB_HOST The GitHub host the token belongs to (such as github.com, acme.ghe.com, or github.example.com)
GH_HOST Same as GITHUB_HOST, so gh commands in later scripts target the same host
GH_ENTERPRISE_TOKEN Set on GitHub Enterprise Server hosts only. Holds the same token, because it is the only token variable gh reads for Enterprise Server

Later Command and Check blocks have access to these credentials. If you authenticate more than once, the most recent credentials become the default.

The block also publishes GITHUB_USER, GITHUB_HOST, GIT_PROVIDER (github) and __AUTHENTICATED as block outputs. Don’t read the token from the outputs. Scripts get it as GITHUB_TOKEN through githubAuthId or gitAuthId, or from the session environment.

A Command or Check block can reference a GitHubAuth block with the githubAuthId prop. Its Run or Check button is then disabled until the referenced block has valid credentials. When several GitHubAuth blocks exist, the block runs with the credentials of the one you named.

<GitHubAuth
id="gh-auth"
title="Authenticate to GitHub"
/>
<Check
id="verify-auth"
title="Verify GitHub Access"
command="gh auth status"
githubAuthId="gh-auth"
successMessage="Successfully authenticated!"
/>
<Command
id="list-repos"
title="List Repositories"
command="gh repo list --limit 10"
githubAuthId="gh-auth"
successMessage="Repositories listed!"
/>

gh reads GITHUB_TOKEN, but git doesn’t. To clone a private repository, use a <GitClone> block with githubAuthId, as in Basic usage.

How environment variables work with references

Section titled “How environment variables work with references”

When a GitHubAuth block authenticates, it sets the session environment variables (GITHUB_TOKEN, GITHUB_USER, GITHUB_HOST, GH_HOST) for all later blocks. It also stores its credentials so that blocks can request them with githubAuthId.

A block that sets githubAuthId gets the referenced block’s credentials as environment variables that override the session values for that block only. This holds when several GitHubAuth blocks have different credentials, when another auth block set the session GITHUB_TOKEN, and when the user later re-authenticated with different credentials.

Without githubAuthId, a block uses whatever GITHUB_TOKEN is in the session environment, set by the most recent authentication.

By default, GitHubAuth detects existing credentials from environment variables and the GitHub CLI. The detectCredentials prop changes this.

With no configuration, GitHubAuth tries these sources in order:

{/* Same as detectCredentials={['env', 'cli']} */}
<GitHubAuth id="gh-auth" />
  1. Environment variables. GITHUB_TOKEN or GH_TOKEN, or GH_ENTERPRISE_TOKEN or GITHUB_ENTERPRISE_TOKEN for a GitHub Enterprise Server host, following the host binding rules.
  2. The GitHub CLI. gh auth token --hostname <host>.

If either source provides valid credentials, authentication succeeds without user input. If neither does, users see the manual OAuth and PAT interface.

Source Description
'env' Check standard env vars (GITHUB_TOKEN, GH_TOKEN, and GH_ENTERPRISE_TOKEN, GITHUB_ENTERPRISE_TOKEN for GitHub Enterprise Server)
{ env: { prefix: 'PREFIX_' } } Check prefixed env vars (PREFIX_GITHUB_TOKEN, PREFIX_GH_TOKEN, and so on), bound to a host by PREFIX_GH_HOST
'cli' Check GitHub CLI (gh auth token --hostname <host>)
{ block: 'block-id' } Use token from a Command block’s output

Check only environment variables:

<GitHubAuth
id="gh-auth"
detectCredentials={['env']}
/>

Check prefixed environment variables such as PROD_GITHUB_TOKEN:

<GitHubAuth
id="gh-auth"
detectCredentials={[{ env: { prefix: 'PROD_' } }, 'cli']}
/>

A prefix must follow these rules:

  • Uppercase letters, numbers, and underscores only
  • Must start with a letter
  • Must end with an underscore, as in PROD_

A prefixed source reads only PREFIX_GITHUB_TOKEN or PREFIX_GH_TOKEN, or PREFIX_GH_ENTERPRISE_TOKEN or PREFIX_GITHUB_ENTERPRISE_TOKEN for a GitHub Enterprise Server host. PREFIX_GH_HOST, and not the unprefixed GH_HOST, decides which host they are bound to, by the same rules as for the unprefixed variables.

To use a different prefix in automated tests, for example to avoid conflicts with local credentials, set env_prefix on the step in runbook_test.yml:

runbook_test.yml
version: 1
tests:
- name: happy-path
steps:
- block: gh-auth
env_prefix: CI_ # Checks CI_GITHUB_TOKEN, CI_GH_TOKEN
expect: success

Without env_prefix, the test framework checks RUNBOOKS_GITHUB_TOKEN first for a github.com block, which lets you keep test credentials separate from the ones in your local environment. See Testing GitHubAuth blocks.

To have the user always sign in on the block:

<GitHubAuth
id="gh-auth"
detectCredentials={false}
/>

The block then ignores environment tokens such as GITHUB_TOKEN and the user’s gh login, and doesn’t offer to check for them again. The user signs in with OAuth or a personal access token. See Disabling detection.

Use a token generated by a previous Command block:

<Command
id="fetch-token"
path="scripts/get-token.sh"
title="Fetch GitHub Token"
/>
<GitHubAuth
id="dynamic-auth"
title="GitHub Access"
detectCredentials={[{ block: 'fetch-token' }]}
/>

The Command script should output the token. The sensitive: prefix masks it in the Command’s View Outputs, and GitHubAuth still reads it as GITHUB_TOKEN:

#!/usr/bin/env bash
# Example: fetch token from a secrets manager
TOKEN=$(vault read -field=token secret/github)
echo "sensitive:GITHUB_TOKEN=$TOKEN" >> "$RUNBOOK_OUTPUT"

Sources are tried in the order you list them. If the referenced block has not run yet, GitHubAuth shows Waiting for fetch-token to run…, with a link to that block, and detection pauses there without moving to a later source. If the block ran without writing a usable GITHUB_TOKEN, for example because the script failed, detection moves on to the next source, and the manual sign-in form appears if none of them finds a credential.

A GitHub token’s permissions control what it can do. How they are set depends on the authentication method:

Method How permissions are set
Environment Variable Permissions are inherited from however the token was originally created
GitHub CLI Permissions are inherited from gh auth login. Runbooks warns if the repo scope is missing
OAuth The runbook author specifies OAuth scopes with the oauthScopes prop, and the user approves them on GitHub
Personal Access Token (PAT) User configures permissions when creating the token on GitHub

When users authenticate via OAuth, the oauthScopes prop controls what permissions are requested. Users see these scopes on GitHub’s authorization page and must approve them.

The default is ["repo"], which grants full read/write access to repositories. Request narrower scopes if your runbook needs less:

Scope Description
repo Full access to private and public repositories (default)
public_repo Access to public repositories only
repo:status Read/write commit status (useful for CI)
read:org Read organization membership

For a complete list, see GitHub’s OAuth scopes documentation.

<GitHubAuth
id="gh-auth"
title="Limited GitHub Access"
oauthScopes={["public_repo", "read:org"]}
/>

When users authenticate with a PAT, the token’s permissions are determined by how the user created it on GitHub. Runbook authors cannot control PAT permissions.

Classic PATs use the same scopes as OAuth, such as repo and read:org. Fine-grained PATs set permissions per repository.

If your runbook requires specific permissions, list them in the runbook so users know what to select when creating their PAT.

A token with an expiration date gets a warning on the block before it expires; see Expiring tokens.

The block’s “create a token” link follows the selected host: https://<host>/settings/personal-access-tokens/new on github.com and GHE.com tenants, and https://<host>/settings/tokens/new on GitHub Enterprise Server.

When GitHubAuth detects credentials from environment variables or GitHub CLI, those tokens retain whatever permissions they were created with. If a detected token lacks required permissions, operations may fail at runtime.

  1. Gruntwork never sees your credentials. Authentication happens between your machine and GitHub. Tokens are not sent to Gruntwork servers.
  2. Tokens stay with their host. A token is sent only to the GitHub host it was issued for.
  3. Credentials are encrypted at rest. Tokens are stored in your local Runbooks session, which Runbooks saves encrypted with the operating system’s credential store. See What is encrypted.
Method How it works
Environment Variable Runbooks reads GITHUB_TOKEN from your local environment.
GitHub CLI Runbooks calls gh auth token locally, which reads the token from the CLI’s own storage.
OAuth Uses GitHub’s Device Authorization Grant. GitHub sends the token to the Runbooks app on your machine. No Gruntwork server is involved.
Personal Access Token You paste the token into the Runbooks app, which sends it only to the GitHub host shown in the block.

When you authenticate via OAuth:

  1. Runbooks, running on your machine, requests a device code from GitHub
  2. You open https://<host>/login/device (github.com/login/device by default) and enter the code. GitHub keeps the code valid for 15 minutes by default, and Runbooks keeps checking for your approval until it expires. If it expires before you approve, start the sign-in again.
  3. You see “Gruntwork Runbooks wants to access your account” (or the name of your enterprise’s OAuth app) and approve
  4. GitHub sends the token to the Runbooks app on your machine

Gruntwork has no server in this flow.

Malicious runbook with custom oauthClientId

Section titled “Malicious runbook with custom oauthClientId”

Scenario. An attacker publishes a runbook with oauthClientId="attacker-app-id".

What happens. GitHub’s authorization page shows “Attacker’s App” where it would normally show “Gruntwork Runbooks”. If you approve, the token still goes to your machine. The attacker registered an OAuth app but has no server receiving tokens.

Risk. None. The token goes from GitHub to your machine, so the attacker cannot intercept it. The only effect is that “Attacker’s App” appears in your GitHub authorized apps list, where you can revoke it.

Mitigation. A custom client ID can still indicate a suspicious runbook, so Runbooks displays a warning when oauthClientId differs from the default. Check the app name on GitHub’s authorization page before approving.

Scenario. An attacker publishes a runbook that tries to redirect OAuth to a fake GitHub login page, for example with oauthBaseUrl="https://github-login.attacker.com".

What happens. Nothing. oauthBaseUrl is not a prop. GitHubAuth only communicates with the GitHub host shown in the block (github.com unless host or the host picker says otherwise), always over HTTPS.

Risk. None.

Scenario. An attacker publishes a runbook with host="github-login.attacker.com", hoping to collect tokens from your environment or GitHub CLI.

What happens. The block shows the host it is authenticating to. Environment tokens are only released to the host they are bound to: GITHUB_TOKEN to github.com or the *.ghe.com host in GH_HOST, and GH_ENTERPRISE_TOKEN only to the Enterprise Server host in GH_HOST. GitHub CLI detection only returns a token you stored for that exact host with gh auth login --hostname. OAuth needs an app registered on that host.

Risk. Low. Only a token you type or paste into the block could reach the attacker’s host.

Mitigation. Check the host shown in the block before pasting a personal access token or approving a sign-in.

Scenario. A runbook requests excessive permissions with oauthScopes={["repo", "admin:org", "delete_repo"]}.

What happens. GitHub’s authorization page displays the requested scopes. Runbooks doesn’t show them before you sign in. If you approve without reviewing them, you grant broader access than needed.

Risk. Low. GitHub displays the requested scopes before you grant access.

Mitigation. Review the scopes on GitHub’s authorization page before approving. Be suspicious of runbooks requesting admin:*, delete_repo, or write:* scopes beyond the default repo.

Gruntwork never has your tokens, so there is nothing to revoke from Gruntwork. Revoking at GitHub invalidates the token itself, which is useful when you are done with a runbook or suspect a token was exposed.

You can manage authorizations at:

On an enterprise host, use the same paths on that host, for example https://github.example.com/settings/tokens.

Runbooks keeps a token in your local session, encrypted, until you delete the session. Quitting Runbooks or resetting the session doesn’t delete it.