mirror of
https://github.com/github/spec-kit.git
synced 2026-07-04 04:45:43 +08:00
* 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.
209 lines
5.7 KiB
Markdown
209 lines
5.7 KiB
Markdown
# 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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
> ```bash
|
|
> 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:**
|
|
|
|
```json
|
|
{
|
|
"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`).
|
|
|
|
```json
|
|
{
|
|
"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:**
|
|
|
|
```json
|
|
{
|
|
"hosts": ["dev.azure.com"],
|
|
"provider": "azure-devops",
|
|
"auth": "basic-pat",
|
|
"token_env": "AZURE_DEVOPS_PAT"
|
|
}
|
|
```
|
|
|
|
**Example — Azure CLI (interactive login):**
|
|
|
|
```json
|
|
{
|
|
"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):**
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```json
|
|
{
|
|
"providers": [
|
|
{
|
|
"hosts": [
|
|
"github.com",
|
|
"api.github.com",
|
|
"raw.githubusercontent.com",
|
|
"codeload.github.com"
|
|
],
|
|
"provider": "github",
|
|
"auth": "bearer",
|
|
"token_env": "GH_TOKEN"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
To use it:
|
|
|
|
```bash
|
|
mkdir -p ~/.specify
|
|
# Copy the JSON above into ~/.specify/auth.json
|
|
chmod 600 ~/.specify/auth.json
|
|
```
|