Backstage CLI Auth 模块实战用 OAuth 2.0 PKCE 实现 CLI 与 Backstage 实例的安全认证【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstagebackstage/cli-module-auth是 Backstage 官方 CLI 的认证模块为backstage-cli提供与 Backstage 实例之间的登录、登出与凭证管理能力。本文将以 docs/tooling/cli/module-auth.md 为主线结合仓库中 packages/cli-module-auth 的完整源码系统讲解auth login、auth logout、auth show、auth list、auth print-token、auth select六个命令的用法、底层 OAuth 2.0 PKCE 授权流程以及凭证的本地存储机制。读完本文你将掌握如何让 CLI 安全地访问多个 Backstage 实例并在脚本与 CI 流水线中复用访问令牌。模块概览认证能力的承载者backstage/cli-module-auth是一个 CLI 模块CLI Module通过createCliModule注册到 Backstage CLI 的命令体系中。从 模块入口 可以看到它一次性注册了 6 个auth子命令命令说明auth login登录 CLI 到某个 Backstage 实例auth logout退出登录并清除本地凭证auth show显示某个已认证实例的详情auth list列出所有已认证实例auth print-token向 stdout 输出访问令牌必要时自动刷新auth select选择默认实例这些命令获取到的访问令牌会被其他 CLI 命令复用例如 actions 命令 在调用 Backstage 后端 API 时使用认证模块产出的令牌。认证原理OAuth 2.0 Authorization Code PKCE该模块采用OAuth 2.0 Authorization Code PKCEProof Key for Code Exchange流程获取访问令牌而不是简单的用户名密码。之所以选择 PKCE是因为 CLI 本质上是一个公开客户端无法安全保管 client secretPKCE 通过动态生成的 code verifier 与 code challenge 防止授权码被截获后重放。PKCE 的核心实现在 pkce.ts 中可以看到两个关键函数generateVerifier(length 64)使用crypto.randomBytes生成随机字节并做 base64url 编码得到 code verifier长度限制在 43128 字符内challengeFromVerifier(verifier)对 verifier 做 SHA-256 哈希后 base64url 编码得到 code challenge即S256变换方法。export function generateVerifier(length 64): string { const bytes crypto.randomBytes(Math.max(32, Math.min(96, length))); return base64url(bytes); } export function challengeFromVerifier(verifier: string): string { const hash crypto.createHash(sha256).update(verifier).digest(); return base64url(hash); }授权请求参数在 login.ts 的buildAuthorizeUrl中可以看到完整的授权请求参数authorize.searchParams.set(client_id, clientId); authorize.searchParams.set(redirect_uri, redirectUri); authorize.searchParams.set(response_type, code); authorize.searchParams.set(scope, openid offline_access); authorize.searchParams.set(state, state); authorize.searchParams.set(code_challenge, challenge); authorize.searchParams.set(code_challenge_method, S256);其中scope为openid offline_accessopenid用于获取用户身份offline_access用于获取 refresh token保证访问令牌过期后可以静默续期state32 字节随机数的 hex 编码crypto.randomBytes(32).toString(hex)用于防止 CSRF回调时若 state 不匹配会直接抛出State mismatchcode_challenge_method为S256与前面 SHA-256 的实现一一对应。令牌交换与刷新授权码拿到后CLI 通过本地回调服务器换取令牌grant_typeauthorization_code附上code_verifier令牌响应格式在 auth.ts 中用 Zod schema 校验包含access_token、token_type、expires_in和可选的refresh_token。访问令牌的刷新逻辑同样在auth.tsaccessTokenNeedsRefresh当令牌距离过期时间不足2 分钟Date.now() 2 * 60_000时视为需要刷新refreshAccessToken向实例的/api/auth/v1/token端点以grant_typerefresh_token发起 POST 请求成功后将新的 access token以及可能轮换的 refresh token写回系统密钥存储并更新过期时间戳若 refresh token 不存在会抛出Access token is expired and no refresh token is available。刷新过程在withMetadataLock中执行避免多进程并发写坏元数据文件。前置条件启用 CLI 认证要使用本模块目标 Backstage 实例必须开启 CLI 认证支持。CLI 的检测方式是请求实例的 well-known 端点/api/auth/.well-known/oauth-client/cli.json在 login.ts 中clientId被设置为该端点 URL登录时先fetch它如果返回非 2xx会抛出Server does not support CLI authentication. Ensure CIMD is enabled on the backend.也就是说后端需要启用 CIMDClient-Initiated Mutual Device / CLI 认证相关能力才能配合本模块工作。部署 Backstage 时若需使用 CLI 认证请确保后端的 auth 插件支持该 well-known 端点。实例名Instance Names机制每个已认证的 Backstage 实例都存放在一个你自己命名的短标签下其他命令通过--instance name引用它例如--instance production。命名规则若登录时未指定名称CLI 会从后端 URL 的 hostname 派生如https://backstage.example.com派生为backstage.example.com见login.ts中的deriveInstanceName实例名必须匹配正则^[a-zA-Z0-9._:-]$否则元数据写入会被 Zod schema 拒绝见 storage.ts。auth login登录 CLI 到 Backstage 实例启动 OAuth 授权流程打开浏览器完成认证随后将凭证保存到本地。Usage: backstage-cli auth login [options] Log in the CLI to a Backstage instance Options: --backendUrl url Backend base URL --noBrowser Do not open browser automatically --instance name A short name for this instance, used to refer to it in other auth commands. Defaults to the backend URL hostname.交互式登录的 URL 发现逻辑不带任何参数运行时命令是交互式的pickBaseUrl函数。它会扫描当前目录下的这些文件来发现后端地址app-config.yamlapp-config.*.yamlpackages/*/app-config.yamlpackages/*/app-config.*.yaml读取其中的backend.baseUrl作为候选让你选择或手动输入。若已有已认证实例还会先询问是复用已有实例还是新增实例promptForInstance。实例名由 URL 的 host 自动派生。浏览器打开方式openInBrowser根据平台调用不同命令macOS 用openWindows 用powershell Start-ProcessLinux 用xdg-open。源码注释特别说明不使用react-dev-utils/openBrowser因为它会二次编码 URL 参数导致登录链接损坏。示例登录交互式yarn backstage-cli auth login登录指定后端 URLyarn backstage-cli auth login --backendUrl https://backstage.example.com登录并命名实例便于后续引用yarn backstage-cli auth login --backendUrl https://backstage.example.com --instance production不自动打开浏览器授权 URL 会打印到终端手动打开yarn backstage-cli auth login --backendUrl https://backstage.example.com --noBrowser登录成功后的持久化登录成功后persistInstanceaccess token 与 refresh token 写入系统密钥存储若服务器未返回 refresh token会向 stderr 打印警告You will need to re-authenticate when the access token expires实例元数据名称、baseUrl、clientId、issuedAt、accessTokenExpiresAt 等写入 YAML 元数据文件复用已有实例时会保留其selected与metadata字段因此重新登录不会丢失默认实例标记。auth logout退出登录并清除凭证Usage: backstage-cli auth logout [options] Log out the CLI and clear stored credentials Options: --instance name Name of the instance to log out登出流程见 logout.ts若指定了--instance则直接使用否则交互式选择pickInstance若有 refresh token先向实例的/api/auth/v1/revoke端点发送token_type_hintrefresh_token的撤销请求——这是best-effort行为遵循 RFC 7009失败会被捕获忽略从密钥存储中删除 access token 与 refresh token从 YAML 元数据文件中移除该实例记录输出Logged out。示例yarn backstage-cli auth logout --instance production交互式登出yarn backstage-cli auth logoutauth show查看实例与当前用户详情Usage: backstage-cli auth show [options] Show details of an authenticated instance Options: --instance name Name of the instance to show该命令show.ts通过CliAuth.create({ instanceName })获取访问令牌过期会自动刷新然后请求实例的/api/auth/v1/userinfo端点输出当前用户身份claims.sub与 ownership 实体引用claims.entUser: user:default/example-user Ownership: - group:default/team-a - group:default/team-b示例查看默认实例yarn backstage-cli auth show查看指定实例yarn backstage-cli auth show --instance productionauth list列出已认证实例Usage: backstage-cli auth list List authenticated instances默认实例以星号*标记见 list.ts 中inst.name selected?.name ? * : 。若没有任何实例向 stderr 输出No instances found。示例yarn backstage-cli auth list输出示例* production - https://backstage.example.com staging - https://backstage-staging.example.comauth print-token为脚本与流水线输出访问令牌Usage: backstage-cli auth print-token [options] Print an access token to stdout (auto-refresh if needed) Options: --instance name Name of the instance to use实现上printToken.ts只做三件事创建CliAuth上下文、调用auth.getAccessToken()内部会依据过期前 2 分钟规则自动刷新、把令牌打印到 stdout。正因为令牌过期会自动刷新该命令非常适合写入脚本与 CI 流水线。示例——打印默认实例的令牌yarn backstage-cli auth print-token示例——结合 curl 调用 Backstage APIcurl -H Authorization: Bearer $(yarn backstage-cli auth print-token) \ https://backstage.example.com/api/catalog/entities示例——指定命名实例yarn backstage-cli auth print-token --instance stagingauth select切换默认实例Usage: backstage-cli auth select [options] Select the default instance Options: --instance name Name of the instance to selectselect决定其他 auth 命令在未传--instance时使用哪个实例。内部调用setSelectedInstancestorage.ts将目标实例的selected置为true其余实例置为false若名称不存在则抛出Unknown instance name。切换成功后向 stderr 输出Selected instance name。不传--instance时交互式选择。示例yarn backstage-cli auth select --instance production交互式选择yarn backstage-cli auth select凭证存储元数据与令牌分离认证状态分两处存储详见 storage.ts实例元数据YAML 文件文件路径为~/.config/backstage-cli/auth-instances.yamlLinux/macOS具体由getMetadataFilePath决定优先使用环境变量XDG_CONFIG_HOMEWindows 下使用%APPDATA%即AppData/Roaming否则使用~/.config。该 YAML 文件保存实例名、后端 URL、clientId、issuedAt、accessTokenExpiresAt、selected标记以及可选的metadata扩展字段。写入时使用mode: 0o600仅当前用户可读写并通过proper-lockfile加锁withMetadataLock最多重试 5 次保证并发安全。元数据读取会经过 Zod schema 校验解析失败时按空列表处理。令牌系统密钥存储access token 与 refresh token 存放在系统 secret store 中通过getSecretStore()获取与 YAML 元数据文件分离。以服务 键的形式管理secretStore.set(service, accessToken | refreshToken, token)避免把敏感令牌明文写进 YAML 文件。这种元数据 密钥分离的设计让实例清单可读、可审计同时令牌本身受到系统级密钥存储保护是 CLI 凭证管理的最佳实践。小结backstage/cli-module-auth用标准的 OAuth 2.0 Authorization Code PKCES256为 Backstage CLI 提供了完整、可脚本化的认证能力login负责授权登录logout负责撤销并清理show查看身份与 ownershiplist管理多实例视图print-token让脚本与 CI 无缝复用自动刷新的令牌select控制默认实例。实例元数据存于auth-instances.yaml0600 权限 文件锁令牌存于系统密钥存储两者分离保证了安全性与可维护性。如果你在开发自己的 Backstage CLI 插件或需要编写与后端交互的自动化工具这套认证链路源码、登录实现、存储实现本身就是一份可直接参考的实现蓝本。【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考