十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Backstage CLI 认证模块实战指南:@backstage/cli-module-auth 登录、令牌刷新与多实例凭据管理

Backstage CLI 认证模块实战指南:@backstage/cli-module-auth 登录、令牌刷新与多实例凭据管理 Backstage CLI 认证模块实战指南backstage/cli-module-auth 登录、令牌刷新与多实例凭据管理【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage本篇文章围绕 Backstage 官方 CLI 认证模块backstage/cli-module-auth展开系统讲解它如何为 Backstage CLI 提供auth login/auth logout/auth print-token等认证命令包括基于 PKCE OAuth 授权码的完整登录流程、访问令牌的自动刷新机制、多 Backstage 实例的凭据与元数据存储以及每个命令的源码级实现原理。读完本文你将能够独立配置并使用 CLI 登录 Backstage 后端、在脚本中安全获取访问令牌并理解登录数据在本地磁盘上的落盘位置与安全边界。模块定位CLI 层的统一认证入口backstage/cli-module-auth是 Backstage 仓库中角色为cli-module的官方包见 packages/cli-module-auth/package.json它本身不提供独立可执行文件而是通过createCliModule注册到主 CLIbackstage-cli中为命令行工具补上登录、注销、凭证管理能力。其官方定位一句话即可概括CLI module that provides authentication commands for the Backstage CLI, enabling login, logout, and credential management for Backstage instances.与浏览器中基于 Cookie 的会话不同CLI 环境没有浏览器会话上下文因此该模块采用OAuth 2.0 授权码 PKCE refresh token的标准流程先通过本地回调地址完成交互式授权再把访问令牌与刷新令牌安全落盘后续所有需要调用 Backstage 后端 API 的 CLI 插件都能复用这套凭据。模块的命令注册集中在 src/index.ts六个子命令全部挂在auth命名空间下且每个命令都采用懒加载execute.loader只有真正执行时才加载对应模块降低 CLI 启动开销reg.addCommand({ path: [auth, login], description: Log in the CLI to a Backstage instance, execute: { loader: () import(./commands/login) }, });命令总览原 README 给出的命令表是使用本模块的第一手索引完整继承如下CommandDescriptionauth loginLog in the CLI to a Backstage instanceauth logoutLog out the CLI and clear stored credentialsauth showShow details of an authenticated instanceauth listList authenticated instancesauth print-tokenPrint an access token to stdout (auto-refresh if needed)auth selectSelect the default instance从源码结构可以进一步推断出各命令的能力边界命令实现见 src/commandslogin/logout负责建立与销毁一条CLI ↔ Backstage 实例的信任关系list/select/show负责实例与身份信息的查看、切换print-token面向脚本场景把可用的访问令牌直接输出到 stdout。auth login一次完整 OAuth 授权码登录auth login是本模块的核心命令其完整实现位于 src/commands/login.ts。整体流程可以概括为五个阶段解析参数 → 确定目标实例 → 发现 OAuth 客户端 → PKCE 授权与换码 → 持久化。命令行参数参数类型说明--backend-url urlString指定 Backstage 后端 base URL--no-browserBoolean不自动打开浏览器改为打印授权 URL 由用户手动打开--instance nameString为本次登录的实例命名供其他 auth 命令引用三者可以自由组合参数解析优先级从源码中可以清晰读出显式传了--instance时若该实例已存在则复用其baseUrl也可用--backend-url覆盖否则按--backend-url或交互输入确定地址只传--backend-url时实例名由 URL 的 host 自动推导deriveInstanceName返回new URL(url).host两者都不传且本地已有实例时弹出选择列表可复用旧实例或Add new instance...完全没有历史实例时进入 base URL 探测流程。自动探测 backend.baseUrl这是 CLI 体验中非常实用的一处设计pickBaseUrl会扫描当前工作目录下的配置文件模式包括app-config.yaml app-config.*.yaml packages/*/app-config.yaml packages/*/app-config.*.yaml解析出其中的backend.baseUrl作为候选若恰好只有一个候选则直接采用多个候选则交给 inquirer 列表让用户选择也可手动输入。也就是说只要你在 Backstage 应用根目录例如本仓库根目录的 app-config.yaml 中配置了backend.baseUrl运行auth login通常无需任何参数即可继续。后端前置条件OAuth 客户端元数据发现登录的第一步不是直接弹浏览器而是请求GET {backendBaseUrl}/api/auth/.well-known/oauth-client/cli.json这是一个 OAuth 客户端元数据client metadata发现请求。若返回非 2xx命令会直接报错Server does not support CLI authentication. Ensure CIMD is enabled on the backend.这意味着后端必须开启对应的 CLI 认证支持即 auth 后端暴露该/.well-known/oauth-client元数据端点才能使用本模块这是部署侧需要确认的前置条件。PKCE本地回环回调下的安全换码由于 CLI 使用本机回环地址作为 redirect URI代码按 PKCE 规范生成了 verifier 与 challenge。相关实现见 src/lib/pkce.tsexport function generateVerifier(length 64): string { // length in bytes ~ 48 results in 64 base64url chars; keep within 43..128 chars 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); }verifier 通过crypto.randomBytes生成并做 base64url 编码字节长度被钳制在 32~96从而保证字符长度落在 RFC 7636 要求的 43~128 字符区间challenge 使用S256方法即对 verifier 做 SHA-256 后 base64url 编码。随后构造的授权 URLbuildAuthorizeUrl参数如下{authBaseUrl}/v1/authorize ?client_id{authBaseUrl}/.well-known/oauth-client/cli.json redirect_uri{本地回调地址} response_typecode scopeopenid offline_access state{随机 state} code_challenge{challenge} code_challenge_methodS256其中client_id就是前面发现的元数据 URL 本身这是一种无需预注册客户端即可完成 CLI 认证的机制scope固定为openid offline_accessopenid用于换取 userinfo 中的身份声明offline_access用于签发 refresh token长期有效state为 32 字节随机 hex 字符串回调用来校验防 CSRF若不传--no-browser命令会调用平台命令打开浏览器macOS 用open、Windows 用powershell Start-Process、Linux 用xdg-open失败时降级为打印 URL 提示手动打开。令牌交换与持久化用户完成授权后本地回调服务器拿到codestate 校验失败会直接抛State mismatch随即向令牌端点发起交换请求POST {authBaseUrl}/v1/token grant_typeauthorization_code code{code} redirect_uri{回调地址} code_verifier{verifier}请求有 30 秒超时TOKEN_EXCHANGE_TIMEOUT_MS。成功后persistInstance会做两件事把access_token与如有refresh_token写入密钥存储secret store密钥标识为accessToken/refreshToken把实例元数据写入auth-instances.yamlname、baseUrl、clientId、issuedAt、accessTokenExpiresAt等。值得注意的边界行为如果服务端没有返回 refresh token命令不会失败但会向 stderr 打印警告Warning: No refresh token received. You will need to re-authenticate when the access token expires.凭据与实例的落盘位置安全边界这是使用本模块必须了解的部分涉及两类数据实例元数据与密钥。实例元数据auth-instances.yaml见 src/lib/storage.ts实例列表保存在名为auth-instances.yaml的 YAML 文件中目录解析规则为XDG_CONFIG_HOME 已设置 → {XDG_CONFIG_HOME}/backstage-cli/auth-instances.yaml Windows → {APPDATA}/backstage-cli/auth-instances.yaml或 %USERPROFILE%\AppData\Roaming macOS / Linux默认 → ~/.config/backstage-cli/auth-instances.yaml写入时显式指定文件权限为0o600仅属主可读写。每条实例的 schema 由 zod 校验storedInstanceSchema字段与约束为字段约束name非空且匹配^[a-zA-Z0-9._:-]$含非法字符会拒绝写入baseUrl必须是合法 URLclientId非空issuedAt非负整数毫秒时间戳accessTokenExpiresAt非负整数毫秒时间戳selected可选布尔值metadata可选任意键值对供其他模块存放如pluginSources等附加信息由于多进程如多个 CLI 命令并发可能同时读写该文件所有写操作都通过withMetadataLock包一层基于proper-lockfile的文件锁重试策略为 5 次、factor 1.5、100ms~1000ms 退避见 storage.ts 中的 lockfile 配置。令牌密钥系统钥匙串或文件回退访问令牌与刷新令牌属于敏感凭证默认优先写入操作系统密钥环通过keytar在 package.json 的optionalDependencies中声明。若 keytar 不可用例如无桌面环境的最小化服务器则回退为文件存储见 packages/cli-node/src/auth/secretStore.tsXDG_DATA_HOME 已设置 → {XDG_DATA_HOME}/backstage-cli/auth-secrets/{service}/{account}.secret Windows → {APPDATA}/backstage-cli/auth-secrets/... macOS / Linux默认 → ~/.local/share/backstage-cli/auth-secrets/...文件同样以0o600权限写入目录结构按encodeURIComponent(service)/{account}.secret组织service 形如backstage-cli-auth-{instanceName}。这一设计保证了有钥匙串的环境凭据更安全无钥匙串的环境仍可回退使用。auth print-token脚本友好的令牌输出print-token面向 CI、shell 脚本或任何需要以编程方式调用 Backstage API 的场景。其实现极简见 src/commands/printToken.tsconst auth await CliAuth.create({ instanceName: instanceFlag }); const accessToken await auth.getAccessToken(); process.stdout.write(${accessToken}\n);它支持--instance name指定实例否则使用当前选中的默认实例。关键点在于自动刷新getAccessToken()在返回令牌前会判断是否临近过期。刷新阈值见 src/lib/auth.tsexport function accessTokenNeedsRefresh(instance: StoredInstance): boolean { // 2 minutes before expiration return instance.accessTokenExpiresAt Date.now() 2 * 60_000; }即距过期不足 2 分钟即视为需要刷新刷新流程refreshAccessToken为在文件锁保护下重新读取实例从密钥存储取refreshToken取不到则抛错Access token is expired and no refresh token is availablePOST {baseUrl}/api/auth/v1/token携带grant_typerefresh_token30 秒超时用 zod 校验响应access_token、token_type、expires_in必须合法refresh_token可选回写新令牌若响应携带新 refresh token 则一并轮换并更新实例的issuedAt与accessTokenExpiresAt。整个刷新逻辑在 packages/cli-node/src/auth/CliAuth.ts 的CliAuth类中还有一份面向其他 CLI 模块编程复用的等价实现#refreshAccessToken并额外提供getInstanceName()、getBaseUrl()、getMetadata()、setMetadata()等接口方便插件化开发。auth show查看当前身份与所有权show输出当前实例的登录身份信息见 src/commands/show.ts。它先通过CliAuth.create拿到有效访问令牌同样带自动刷新再以 Bearer 方式调用 userinfo 端点GET {baseUrl}/api/auth/v1/userinfo Authorization: Bearer {accessToken}然后打印claims.sub用户主体标识与claims.entownership 实体列表用于权限判定中的我拥有哪些实体User: user:default/guest Ownership: - group:default/backstage这在排查权限permissions问题时非常有用——可以直接确认 CLI 当前以谁的身份在访问后端。auth list与auth select多实例管理CLI 可以同时登录多个 Backstage 实例例如开发环境、预发环境与多个客户的隔离实例。auth listsrc/commands/list.ts遍历本地全部实例当前选中的实例以*前缀标记输出格式为{mark}{name} - {baseUrl}没有任何实例时向 stderr 输出No instances foundauth selectsrc/commands/select.ts通过--instance name或交互选择把某个实例置为默认。底层调用setSelectedInstance见 storage.ts将目标实例标记selected: true其余实例全部清除该标记。默认实例的解析规则在getAllInstances中优先取selected: true的实例若没有显式选中项则默认取列表中的第一个。auth logout干净地注销与撤销令牌logoutsrc/commands/logout.ts执行三步清理撤销刷新令牌若存在 refresh token先POST {baseUrl}/api/auth/v1/revoke携带token_type_hintrefresh_token该请求失败会被静默忽略遵循 RFC 7009 的容错约定注释明确写着ignore errors per RFC 7009删除密钥从密钥存储中删除accessToken与refreshToken删除实例从auth-instances.yaml中移除该实例条目。全部操作都在withMetadataLock文件锁内完成最后向 stdout 输出Logged out。常见问题与排查建议结合源码实现整理几个高频问题的排查路径现象可能原因与处理Server does not support CLI authentication. Ensure CIMD is enabled on the backend.后端 auth 服务未暴露/.well-known/oauth-client/cli.json元数据端点需要先在 Backstage 后端开启 CLI 认证支持可参见仓库 docs/auth/index.md 了解 auth 能力范围Access token is expired and no refresh token is available登录时服务端未签发 refresh token或已被撤销且访问令牌已过期。重新执行auth login即可No instances found. Run auth login to authenticate first.本地没有任何实例记录print-token/show等命令无法解析默认实例先登录登录成功但print-token输出的令牌很快失效若登录时收到 No refresh token received 警告说明后端未下发 refresh token令牌过期后无法自动刷新只能重新登录换了机器或容器环境后实例消失实例与密钥分别存放在~/.config/backstage-cli与~/.local/share/backstage-cli或系统钥匙串中属于用户级本地状态不随项目迁移小结backstage/cli-module-auth用一套标准的 OAuth 授权码 PKCE 流程把CLI 登录 Backstage 后端这件原本繁琐的事收敛成了六个语义清晰的子命令并通过实例元数据 密钥存储的双层设计让多实例切换、令牌自动刷新与安全注销都变得透明可靠。对于要在脚本中调用 Backstage API 的场景auth print-token配合其内置的 2 分钟提前刷新策略足以作为 CI 或自动化任务中稳定的凭据来源而对需要二次开发 CLI 插件的团队CliAuth见 packages/cli-node/src/auth/CliAuth.ts提供了开箱即用的编程接口可直接复用其解析实例、获取与刷新令牌的能力。【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表