<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.
Basic usage
Section titled “Basic usage”<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:
- Environment variables:
GITHUB_TOKENorGH_TOKEN, andGH_ENTERPRISE_TOKENorGITHUB_ENTERPRISE_TOKENfor a GitHub Enterprise Server host. See Environment variables and hosts. - The GitHub CLI (
gh auth token --hostname <host>). Whenghisn’t installed, Runbooks reads the token from gh’shosts.ymlinstead, unless gh keeps it in the system keyring.
If no credentials are detected, users authenticate manually with OAuth or a PAT.
Authentication methods
Section titled “Authentication methods”| 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 |
GitHub Enterprise support
Section titled “GitHub Enterprise support”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.
Pinning the host
Section titled “Pinning the host”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.
Picking a host
Section titled “Picking a host”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, whichgh auth login --hostname <host>writes to) - The host named by the
GH_HOSTenvironment 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:
- The host the user last picked, if a credential for it is still available
- The host named by
GH_HOST github.com
To make an enterprise host show up in the picker, log in to it once with the GitHub CLI:
gh auth login --hostname github.example.comEnvironment variables and hosts
Section titled “Environment variables and hosts”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.
OAuth on enterprise hosts
Section titled “OAuth on enterprise hosts”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.
Block outputs
Section titled “Block outputs”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.
Explicit block references
Section titled “Explicit block references”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.
Credential detection
Section titled “Credential detection”By default, GitHubAuth detects existing credentials from environment variables and the GitHub CLI. The detectCredentials prop changes this.
Default behavior
Section titled “Default behavior”With no configuration, GitHubAuth tries these sources in order:
{/* Same as detectCredentials={['env', 'cli']} */}<GitHubAuth id="gh-auth" />- Environment variables.
GITHUB_TOKENorGH_TOKEN, orGH_ENTERPRISE_TOKENorGITHUB_ENTERPRISE_TOKENfor a GitHub Enterprise Server host, following the host binding rules. - 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.
Detection sources
Section titled “Detection sources”| 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 |
Custom detection order
Section titled “Custom detection order”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.
Test prefix
Section titled “Test prefix”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:
version: 1tests: - name: happy-path steps: - block: gh-auth env_prefix: CI_ # Checks CI_GITHUB_TOKEN, CI_GH_TOKEN expect: successWithout 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.
Disabling detection
Section titled “Disabling detection”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.
From Command output
Section titled “From Command output”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 managerTOKEN=$(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.
Permissions
Section titled “Permissions”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 |
OAuth scopes
Section titled “OAuth scopes”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"]}/>Personal access tokens
Section titled “Personal access tokens”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.
Auto-detected credentials
Section titled “Auto-detected credentials”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.
Security
Section titled “Security”Principles
Section titled “Principles”- Gruntwork never sees your credentials. Authentication happens between your machine and GitHub. Tokens are not sent to Gruntwork servers.
- Tokens stay with their host. A token is sent only to the GitHub host it was issued for.
- 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.
How credentials stay local
Section titled “How credentials stay local”| 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. |
OAuth flow details
Section titled “OAuth flow details”When you authenticate via OAuth:
- Runbooks, running on your machine, requests a device code from GitHub
- 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. - You see “Gruntwork Runbooks wants to access your account” (or the name of your enterprise’s OAuth app) and approve
- GitHub sends the token to the Runbooks app on your machine
Gruntwork has no server in this flow.
Threat analysis
Section titled “Threat analysis”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.
Malicious runbook with custom base URL
Section titled “Malicious runbook with custom base URL”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.
Malicious runbook with custom host
Section titled “Malicious runbook with custom host”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.
Scope escalation
Section titled “Scope escalation”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.
Managing your authorizations
Section titled “Managing your authorizations”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:
- github.com/settings/applications to revoke OAuth authorizations
- github.com/settings/tokens to revoke personal access tokens
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.