Files
github-spec-kit/docs/reference/authentication.md
Si Zengyu 1add20341d fix(extensions,presets,workflows): resolve private GHES release assets via /api/v3 (#3157)
* feat(auth): add github_provider_hosts() to enumerate GHES hosts from auth.json

Assisted-by: Claude Code (model: claude-sonnet-4-6, autonomous)

* fix(extensions): resolve GHES release assets via /api/v3

Generalizes resolve_github_release_asset_api_url to GitHub Enterprise
Server hosts (gated by auth.json github hosts), fixing private GHES
extension/preset downloads. github/spec-kit#3147

Assisted-by: Claude Code (model: claude-sonnet-4-6, autonomous)

* fix(extensions,presets): pass auth.json github hosts into release resolver

Assisted-by: Claude Code (model: claude-sonnet-4-6, autonomous)

* docs(auth): document GHES private catalog + release-asset auth

Assisted-by: Claude Code (model: claude-sonnet-4-6, autonomous)

* fix(presets,workflows): pass auth.json github hosts into remaining release resolvers

Wires preset add --from and workflow add through github_provider_hosts()
so private GHES release assets resolve via /api/v3 there too. github/spec-kit#3147

Assisted-by: Claude Code (model: claude-sonnet-4-6, autonomous)

* test(presets): use module-level io.BytesIO in GHES preset test

Addresses Copilot review on PR #3157: drop unnecessary __import__("io")
in test_preset_add_from_ghes_release_url_resolves_via_api_v3 since io is
already imported at module level.

* fix(github-http): pass through GHES asset API URLs by path shape

Addresses Copilot review on PR #3157. A direct GHES /api/v3 release asset
URL was only returned as already-resolved when its host was in the
allowlist; otherwise the resolver returned None and the caller downloaded
the same URL without 'Accept: application/octet-stream', fetching JSON
metadata instead of the binary.

Gate the passthrough on path shape alone, mirroring the github.com case.
This is safe: passthrough returns the input URL unchanged and the caller
fetches it either way, so no new request to an arbitrary host is induced;
the token stays independently gated by auth.json in open_url. The
allowlist remains the anti-SSRF gate on the tag-lookup resolving path.

Add test_passthrough_for_unlisted_ghes_api_asset_url.
2026-06-25 10:44:30 -05:00

5.7 KiB

Authentication

Specify CLI uses opt-in authentication for HTTP requests to catalog sources, extension downloads, and release checks. No credentials are sent unless you explicitly configure them.

Configuration

Create ~/.specify/auth.json to enable authentication:

{
  "providers": [
    {
      "hosts": ["github.com", "api.github.com", "raw.githubusercontent.com", "codeload.github.com"],
      "provider": "github",
      "auth": "bearer",
      "token_env": "GH_TOKEN"
    }
  ]
}

Security: Restrict the file to owner-only access:

chmod 600 ~/.specify/auth.json

Without this file, all HTTP requests are unauthenticated.

Fields

Each entry in the providers array has the following fields:

Field Required Description
hosts Yes Array of hostnames this entry applies to. Supports exact hostnames, or a leading *. wildcard for subdomains only (for example, *.visualstudio.com). *.visualstudio.com matches foo.visualstudio.com, but not visualstudio.com. Other glob patterns such as *github.com or gith?b.com are not supported.
provider Yes Built-in provider key: github or azure-devops.
auth Yes Auth scheme (see below).
token No Token value (inline). Use token_env instead when possible.
token_env No Environment variable name to read the token from.

For azure-ad auth, additional fields are required:

Field Required Description
tenant_id Yes Azure AD tenant ID.
client_id Yes Service principal client ID.
client_secret_env Yes Environment variable containing the client secret.

Either token or token_env must be set for bearer and basic-pat schemes.

Providers and auth schemes

GitHub (github)

Scheme Header Use for
bearer Authorization: Bearer <token> PATs, fine-grained PATs, OAuth tokens, GitHub App tokens

Example — PAT via environment variable:

{
  "hosts": ["github.com", "api.github.com", "raw.githubusercontent.com", "codeload.github.com"],
  "provider": "github",
  "auth": "bearer",
  "token_env": "GH_TOKEN"
}

GitHub Enterprise Server (GHES)

To use a private catalog or extension hosted on a GitHub Enterprise Server instance, add a github entry listing your GHES host(s). The same entry authenticates both catalog JSON fetches and private release-asset downloads — Specify recognizes the listed hosts as GitHub Enterprise and resolves release downloads through the GHES REST API (/api/v3).

{
  "providers": [
    {
      "hosts": ["ghes.example.com", "raw.ghes.example.com", "codeload.ghes.example.com"],
      "provider": "github",
      "auth": "bearer",
      "token_env": "GH_ENTERPRISE_TOKEN"
    }
  ]
}

List the bare web host (e.g. ghes.example.com) — release-download URLs live there. If your instance uses subdomain isolation, also list the raw. and codeload. subdomains your catalog/extension URLs use. A *.ghes.example.com wildcard matches subdomains but not the bare host, so always include the bare host explicitly.

Azure DevOps (azure-devops)

Scheme Header Use for
basic-pat Authorization: Basic base64(:<PAT>) Personal Access Tokens
bearer Authorization: Bearer <token> Pre-acquired OAuth / Azure AD tokens
azure-cli Authorization: Bearer <token> Token acquired via az account get-access-token
azure-ad Authorization: Bearer <token> Token acquired via OAuth2 client credentials flow

Example — PAT via environment variable:

{
  "hosts": ["dev.azure.com"],
  "provider": "azure-devops",
  "auth": "basic-pat",
  "token_env": "AZURE_DEVOPS_PAT"
}

Example — Azure CLI (interactive login):

{
  "hosts": ["dev.azure.com"],
  "provider": "azure-devops",
  "auth": "azure-cli"
}

Requires az login to have been run beforehand.

Example — Azure AD service principal (CI/automation):

{
  "hosts": ["dev.azure.com"],
  "provider": "azure-devops",
  "auth": "azure-ad",
  "tenant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "client_secret_env": "AZURE_CLIENT_SECRET"
}

Multiple entries

You can configure multiple entries for different hosts or organizations:

{
  "providers": [
    {
      "hosts": ["github.com", "api.github.com", "raw.githubusercontent.com", "codeload.github.com"],
      "provider": "github",
      "auth": "bearer",
      "token_env": "GH_TOKEN"
    },
    {
      "hosts": ["dev.azure.com"],
      "provider": "azure-devops",
      "auth": "basic-pat",
      "token_env": "AZURE_DEVOPS_PAT"
    }
  ]
}

How it works

  1. For each outbound HTTP request, the URL hostname is matched against the hosts patterns in auth.json.
  2. If a match is found, the corresponding provider resolves the token and attaches the appropriate Authorization header.
  3. If the request receives a 401 or 403, the next matching entry is tried.
  4. After all matching entries are exhausted, an unauthenticated request is attempted as a final fallback.
  5. On redirects, the Authorization header is stripped if the redirect target leaves the entry's declared hosts — preventing credential leakage to CDNs or third-party services.

Template

A reference auth.json with GitHub pre-configured:

{
  "providers": [
    {
      "hosts": [
        "github.com",
        "api.github.com",
        "raw.githubusercontent.com",
        "codeload.github.com"
      ],
      "provider": "github",
      "auth": "bearer",
      "token_env": "GH_TOKEN"
    }
  ]
}

To use it:

mkdir -p ~/.specify
# Copy the JSON above into ~/.specify/auth.json
chmod 600 ~/.specify/auth.json