Specify CLI 认证机制详解:auth.json 配置、GitHub / Azure DevOps 鉴权与凭证安全

发布时间:2026/9/6 22:33:13
Specify CLI 认证机制详解:auth.json 配置、GitHub / Azure DevOps 鉴权与凭证安全 Specify CLI 认证机制详解auth.json 配置、GitHub / Azure DevOps 鉴权与凭证安全【免费下载链接】spec-kit Toolkit to help you get started with Spec-Driven Development项目地址: https://gitcode.com/GitHub_Trending/sp/spec-kit本文基于 spec-kit 仓库的认证参考文档完整讲解 Specify CLI 的 opt-in显式启用认证机制如何编写~/.specify/auth.json、hosts/provider/auth等字段的取值与校验规则、GitHub 与 Azure DevOps 各类认证方案bearer、basic-pat、azure-cli、azure-ad的配置示例以及凭证匹配、401/403 回退与重定向凭证剥离的底层实现。读完后你可以为公共 GitHub、GitHub Enterprise Server 或 Azure DevOps 私有目录catalog/扩展extension/预设preset下载正确配置凭据并理解其安全边界。1. 设计原则不配置即不发送凭据Specify CLI 对目录源 HTTP 请求、扩展下载和版本发布检查均采用**可选认证opt-in authentication**模型只有当你显式创建~/.specify/auth.json时CLI 才会向对应主机附加Authorization头该文件不存在时所有 HTTP 请求均以未认证方式发出配置文件中“哪些主机 哪个 provider 哪种认证方案”由你声明provider 类定义“如何认证”Bearer、Basic-PAT 等。这一模型在 src/specify_cli/authentication/init.py 的模块 docstring 中被明确陈述其内置 provider 注册表AUTH_REGISTRY通过_register_builtins()注册了github与azure-devops两个内置 provider。2. 配置文件结构认证配置全部集中在用户主目录下的单个 JSON 文件中mkdir -p ~/.specify # 将下文 JSON 写入 ~/.specify/auth.json chmod 600 ~/.specify/auth.json最小可用的 GitHub 配置{ providers: [ { hosts: [github.com, api.github.com, raw.githubusercontent.com, codeload.github.com], provider: github, auth: bearer, token_env: GH_TOKEN } ] }安全提示建议将文件权限收紧为仅属主可读写chmod 600。这一点并非只是文档建议——从源码看config.py 在加载配置时会检查文件权限位若在 POSIX 系统上该文件对 group/others 可读会发出UserWarning提醒执行chmod 600但不会因此失败。2.1 字段参考providers数组中每个条目的通用字段字段必填说明hosts是该条目适用的主机名数组。仅支持精确主机名或以*.开头的子域通配如*.visualstudio.com。*.visualstudio.com匹配foo.visualstudio.com但不匹配visualstudio.com本身。*github.com、gith?b.com等其他 glob 模式不被支持会在加载时被拒绝。provider是内置 provider 键github或azure-devops。auth是认证方案见下节。token否内联 token 值。可能时优先使用token_env。token_env否存放 token 的环境变量名。azure-ad方案额外要求三个字段字段必填说明tenant_id是Azure AD 租户 ID。client_id是服务主体service principal客户端 ID。client_secret_env是存放 client secret 的环境变量名。bearer与basic-pat方案必须至少设置token或token_env之一。2.2 源码中的校验规则load_auth_config() 对配置的约束比文档表格更具体了解它们可以避免配置不生效host 模式白名单_is_valid_host_pattern()只接受两种形式——精确主机名和*.suffix。它显式拒绝含?、[、]的模式以及*出现在其他位置的写法。注释中说明动机*github.com会匹配github.com.evil.com这类危险形式因此必须禁用。归一化所有hosts值在存储前会被strip().lower()token_env、tenant_id、client_id、client_secret_env等字符串引用字段经_norm()去除首尾空白防止“验证通过但环境变量查不到”的静默故障。provider/scheme 兼容性未知provider或该 provider 不支持的auth值会抛出ValueError错误信息会列出已注册的 provider 或该 provider 支持的方案列表。schema 违规的容错策略文件不存在返回空列表即全部未认证请求JSON 结构错误则抛ValueError而更高层的 HTTP 辅助函数会捕获它、告警后继续以未认证方式运行见 http.py 的_load_config()配置按进程缓存文件最多读取一次。3. Provider 与认证方案3.1 GitHubgithub方案请求头适用场景bearerAuthorization: Bearer tokenPAT、细粒度 PATfine-grained PAT、OAuth token、GitHub App tokenGitHubAuth 类只声明了key github与supported_auth_schemes (bearer,)其auth_headers()对非bearer方案直接抛ValueErrortoken 解析则继承基类默认逻辑优先读entry.token否则读token_env指定的环境变量并对值做strip()。示例 — 通过环境变量注入 PAT{ hosts: [github.com, api.github.com, raw.githubusercontent.com, codeload.github.com], provider: github, auth: bearer, token_env: GH_TOKEN }3.2 GitHub Enterprise ServerGHES若目录或扩展托管在自管的 GHES 实例上只需添加一条github条目列出该实例的主机名。同一条目既用于认证 catalog JSON 拉取也用于私有 release 资产下载——Specify 会识别这些主机为 GitHub Enterprise并将 release 下载解析到 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 } ] }配置要点必须列出裸 web 主机如ghes.example.com因为 release 下载 URL 就挂在它下面若实例使用子域隔离还要把 catalog/扩展 URL 实际用到的raw.、codeload.子域一并列出*.ghes.example.com通配只匹配子域、不匹配裸主机因此裸主机必须显式列出。源码侧可以印证这一机制http.py 的github_provider_hosts()会收集auth.json中所有githubprovider 条目的hosts供 resolve_github_release_asset_api_url() 使用。该函数把浏览器式下载 URLhttps://host/owner/repo/releases/download/tag/asset解析为 REST API 资产 URL主机是否按 GHES 处理正是由这份白名单决定——未列入的主机不会被当作 GHES从而阻止恶意 catalog 诱导向任意主机发起 API 请求。扩展、预设、workflow 的安装路径如 extensions/__init__.py、presets/__init__.py、commands/bundle/__init__.py都通过传入github_provider_hosts()复用这一逻辑。3.3 Azure DevOpsazure-devopsAzureDevOpsAuth 支持四种方案方案请求头适用场景basic-patAuthorization: Basic base64(:PAT)个人访问令牌PATbearerAuthorization: Bearer token预先获取的 OAuth / Azure AD tokenazure-cliAuthorization: Bearer token通过az account get-access-token获取 tokenazure-adAuthorization: Bearer token通过 OAuth2 client credentials 流程获取 token示例 — 环境变量注入 PAT{ hosts: [dev.azure.com], provider: azure-devops, auth: basic-pat, token_env: AZURE_DEVOPS_PAT }示例 — Azure CLI交互式登录{ hosts: [dev.azure.com], provider: azure-devops, auth: azure-cli }要求此前已执行az login。从源码看_acquire_via_az_cli()会以 30 秒超时执行az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --output json并解析accessToken字段它特意用shutil.which尊重 Windows 的PATHEXT解析出绝对路径的az避免工作目录中一个名为az.cmd的恶意文件被当成凭证操作执行——任何失败都会返回None并回退到下一个策略而不是抛出异常。示例 — Azure AD 服务主体CI/自动化{ 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 }_acquire_via_client_credentials()会向https://login.microsoftonline.com/tenant_id/oauth2/v2.0/token发起 POST请求client_credentials授权与499b84ac-1321-427f-aa17-267ca6975798/.default作用域。该请求有三层防护拒绝一切 307/308 重定向否则 POST 体中的client_secret会被原样转发到别处响应经read_response_limited按上限读取超大响应视为失败网络故障、JSON 解析失败等一律返回None落入回退。4. 多条目配置可以同时配置多个条目以覆盖不同主机或组织条目按数组顺序匹配{ 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 } ] }5. 工作机制与源码级实现文档描述的运行时行为可概括为五步下面逐条对照 src/specify_cli/authentication/http.py 的实现主机匹配每个出站请求先用 find_entries_for_url() 解析 URL 的 hostname与auth.json中各条目的hosts模式比对*.前缀通配对hostname.endswith(pattern[1:])求值因此只匹配子域。畸形 authority如未闭合的 IPv6 方括号被视为无主机名返回空匹配而非抛异常。附加凭证命中后由对应 provider 的resolve_token()取 tokenauth_headers()构造Authorization头。build_request()中有一个防绕过细节extra_headers里的Authorization键会被剥除认证头最后合并外部无法覆盖。401/403 回退open_url() 遍历所有匹配条目某一轮收到 401/403 时关闭响应、尝试下一条目其他错误404、500、网络错误立即抛出。未认证兜底所有条目用尽或无匹配后以无认证请求作为最后回退。重定向凭证剥离每次尝试都安装隔离的 opener 并挂上_StripAuthOnRedirect处理器。重定向时它会校验目标必须为带主机名的 HTTPS仅允许环回地址之间使用 HTTP若新主机不在该条目的声明主机内、或发生 HTTPS→HTTP 降级则从请求和unredirected_hdrs中同时移除Authorization防止凭据泄漏到 CDN 或第三方服务目标 URL 畸形则抛URLError交由上层下载错误处理。隔离 opener 的动机是代码注释中说明的open_url()每次自建 opener使得进程中若有全局urllib.request.install_opener也无法替换掉重定向守卫。6. 与其他鉴权路径的关系仓库中另有一条独立的轻量路径build_github_request() 直接读取GITHUB_TOKEN/GH_TOKEN环境变量但只针对内置的四个 GitHub 官方域名GITHUB_HOSTSgithub.com、api.github.com、raw.githubusercontent.com、codeload.github.com附加 Bearer 头非 GitHub 主机一律不加避免向第三方主机泄漏凭据。从源码结构看配置驱动的open_url()/build_request()是扩展、预设、bundle、workflow 安装时下载资产的主通道tests/test_authentication.py及tests/http_helpers.py对其行为有测试覆盖而build_github_request()服务于对 GitHub 官方域名的简单请求场景。7. 快速上手模板一份预置 GitHub 配置的参考auth.json{ providers: [ { hosts: [ github.com, api.github.com, raw.githubusercontent.com, codeload.github.com ], provider: github, auth: bearer, token_env: GH_TOKEN } ] }启用步骤mkdir -p ~/.specify # 将上方 JSON 复制到 ~/.specify/auth.json chmod 600 ~/.specify/auth.json # 使用前确保 GH_TOKEN 已导出小结spec-kit 的认证体系围绕单一事实源~/.specify/auth.json展开以主机白名单控制凭据作用范围以 provider/scheme 双层键约束认证方式并在请求链路上实现了 401/403 逐条目回退、未认证兜底、重定向剥离与跨域防泄漏等安全行为。对使用公共 GitHub 的用户最小配置即是一条bearertoken_env条目对 GHES 用户额外列出裸主机即可同时打通 catalog 拉取与私有 release 下载对 Azure DevOps 用户则可根据交互或 CI 场景在basic-pat、azure-cli、azure-ad三种动态/静态方案中选择。【免费下载链接】spec-kit Toolkit to help you get started with Spec-Driven Development项目地址: https://gitcode.com/GitHub_Trending/sp/spec-kit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考