Backstage 接入 Auth0 身份认证:Provider 配置、后端模块与登录实践全指南
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
Auth0 是 Backstage 内置的 OAuth 认证 Provider 之一,可通过标准的 OAuth 2.0 / OIDC 流程让用户使用 Auth0 账号登录开发门户。本文以仓库中 docs/auth/auth0/provider.md 为核心骨架,结合@backstage/plugin-auth-backend-module-auth0-provider的源码实现与测试用例,完整讲解 Auth0 应用创建、app-config.yaml配置、后端模块安装、前端登录页接入与身份解析器(Sign-in Resolver)的使用,帮助你在自己的 Backstage 实例中快速、安全地落地 Auth0 登录。
前置认知:Backstage 认证体系中的 Auth0
在 Backstage 的认证体系中(详见 docs/auth/index.md),内置认证 Provider 处理特定服务的认证流程(包括所需 scope、回调等),负责两件事:
- Sign-in(登录身份识别):将 Auth0 外部身份映射为 Backstage 用户身份;
- 访问委托(Access Delegation):代表用户向第三方资源请求访问权限。
Auth0 是核心库中内置的 Provider 之一,前端由core-plugin-api提供认证 API 引用,后端由独立的后端模块@backstage/plugin-auth-backend-module-auth0-provider提供实现(源码见 plugins/auth-backend-module-auth0-provider)。该模块通过createBackendModule注册providerId: 'auth0',并复用@backstage/plugin-auth-node中的createOAuthProviderFactory与commonSignInResolvers(见 module.ts)。
第一步:在 Auth0 控制台创建应用
- 登录 Auth0 Dashboard;
- 导航到Applications;
- 创建 Application:
- Name:
Backstage(或你的自定义应用名); - Application type:
Single Page Web Application;
- Name:
- 进入Settings标签页;
- 在
Application URIs>Allowed Callback URLs中加入回调地址:
http://localhost:7007/api/auth/auth0/handler/frame- 点击Save Changes保存。
回调路径中的
/handler/frame是 Backstage 前端 OAuth 弹窗/iframe 流程使用的回调端点。若前端配置了实验性重定向流程(enableExperimentalRedirectFlow),回调地址规则会有所不同,可参见 docs/auth/index.md。
第二步:在 app-config.yaml 中配置 Auth0 Provider
在app-config.yaml的根级auth配置下添加 Provider 配置:
auth: environment: development providers: auth0: development: clientId: ${AUTH_AUTH0_CLIENT_ID} clientSecret: ${AUTH_AUTH0_CLIENT_SECRET} domain: ${AUTH_AUTH0_DOMAIN_ID} audience: ${AUTH_AUTH0_AUDIENCE} connection: ${AUTH_AUTH0_CONNECTION} connectionScope: ${AUTH_AUTH0_CONNECTION_SCOPE} organization: ${AUTH_AUTH0_ORGANIZATION_ID} ## uncomment to let Auth0 determine whether to prompt the user # prompt: auto ## uncomment to set lifespan of user session # sessionDuration: { hours: 24 } # supports `ms` library format (e.g. '24h', '2 days'), ISO duration, "human duration" as used in code session: secret: ${AUTH_SESSION_SECRET}必填配置项
| 配置键 | 说明 |
|---|---|
clientId | Auth0 Application 的 Client ID,在 Auth0 Application 页面查看 |
clientSecret | Auth0 Application 的 Client Secret,在 Auth0 Application 页面查看 |
domain | Auth0 Application 的 Domain,在 Auth0 Application 页面查看(如your-tenant.auth0.com) |
session.secret | 会话密钥,用于对应用设置的 Cookie 进行签名和/或加密以维持会话状态,应替换为仅你的应用知晓的、足够长且复杂唯一的随机字符串 |
Auth0 登录依赖会话(session),因此必须为session配置secret。
从源码看,clientId、clientSecret、domain三个键在 authenticator.ts 中通过config.getString(...)强制读取,缺任一配置都会在模块初始化阶段直接报错;而audience、connection、connectionScope、prompt、organization均通过config.getOptionalString(...)读取(见 authenticator.ts),为可选配置。
可选配置项
| 配置键 | 说明 |
|---|---|
audience | Token 的目标接收方(intended recipients)标识 |
connection | 社交身份提供商名称,可用社交连接列表参见 Auth0 Social Connections 市场 |
connectionScope | 交互式 Token 请求中的附加 scope,必须与connection参数组合使用 |
prompt | 控制发送给 Auth0 的 prompt 参数。设为auto时省略该参数,由 Auth0 自行决定是否提示用户;其他值原样透传给 Auth0。默认值为consent |
sessionDuration | 用户会话的存活时长,支持ms库格式(如'24h'、'2 days')、ISO 时长格式及代码中使用的"人类可读时长"格式 |
organization | 指定登录流程中要定向的特定组织 ID |
callbackUrl | 覆盖默认回调地址(可选) |
federatedLogout | 是否执行联合登出,同时清除 Auth0 会话与上游 IdP 会话,默认false |
以上配置项与模块中的类型定义一一对应,完整 schema 见 config.d.ts。其中clientSecret在 schema 中被标记为@visibility secret,clientId标记为@visibility frontend——前者确保敏感信息不会下发到前端。
配置项的底层行为:源码级解读
在 authenticator.ts 的initialize阶段:
prompt未配置时默认取'consent'(config.getOptionalString('prompt') ?? 'consent');- 配置
prompt: auto时,start阶段会通过...(prompt !== 'auto' ? { prompt } : {})将该参数从授权请求中完全省略(见 authenticator.ts); audience、connection、connectionScope在start与authenticate阶段都会作为附加参数传给 Auth0;start阶段固定附带accessType: 'offline',用于请求 Refresh Token,支撑后续的 token 刷新与长期会话。
此外,由于passport-auth0强制options.state = true,而passport-oauth2在 state 开启时需要 express-session 存储 state 参数,实现中通过一个与passport-oauth2中NullStore行为一致的 StateStore 桩实现规避了对 express-session 的依赖(见 authenticator.ts),这使得 Backstage 后端无需额外引入会话中间件即可与 Auth0 集成。
domain的另一处关键作用体现在 strategy.ts:模块基于domain动态拼接 Auth0 的四个端点——https://{domain}/authorize(授权端点)、https://{domain}/oauth/token(令牌端点)、https://{domain}/userinfo(用户信息端点)与https://{domain}/api。
组织登录(Organization)的特殊行为
配置organization后,strategy.ts 会校验请求 query 中携带的organization参数:若请求中的组织与策略中配置的组织不一致,会抛出InputError("Organization mismatch. ...")。同时该策略还支持透传invitation(组织邀请)、screen_hint、login_hint等请求参数到 Auth0 授权流程。
第三步:后端安装 Auth0 Provider 模块
从 Backstage 根目录执行:
yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-auth0-provider然后在packages/backend/src/index.ts中注册模块:
import { createBackend } from '@backstage/backend-defaults'; //... backend.add(import('@backstage/plugin-auth-backend')); // highlight-add-next-line backend.add(import('@backstage/plugin-auth-backend-module-auth0-provider')); //...模块的默认导出是authModuleAuth0Provider(见 index.ts),注册时声明了对authProvidersExtensionPoint与coreServices.cache的依赖:前者用于向 auth 插件注册auth0Provider,后者用于 token 刷新时的用户资料缓存(详见下文"刷新与缓存"小节)。
第四步:前端登录页接入
新版前端系统(New Frontend System)
在packages/app/src/App.tsx中通过SignInPageBlueprint定义登录页,并传入 Auth0 的auth0AuthApiRef:
import { createApp } from '@backstage/frontend-defaults'; import catalogPlugin from '@backstage/plugin-catalog/alpha'; import { navModule } from './modules/nav'; import { auth0AuthApiRef } from '@backstage/core-plugin-api'; import { SignInPageBlueprint } from '@backstage/plugin-app-react'; import { SignInPage } from '@backstage/core-components'; import { createFrontendModule } from '@backstage/frontend-plugin-api'; const signInPage = SignInPageBlueprint.make({ params: { loader: async () => props => ( <SignInPage {...props} provider={{ id: 'auth0-auth-provider', title: 'Auth0', message: 'Sign in using Auth0', apiRef: auth0AuthApiRef, }} /> ), }, }); export default createApp({ features: [ catalogPlugin, navModule, createFrontendModule({ pluginId: 'app', extensions: [signInPage], }), ], });SignInPage会在应用其他路由渲染前呈现,负责提供当前用户身份;用户成功登录后通过onSignInSuccess回调获得合法的 Backstage 身份,其余应用才被渲染(参见 docs/auth/index.md)。
若同时启用多种登录方式(例如开发环境允许 Guest 登录),可将provider换成providers数组;也可基于configApi.getString('auth.environment')条件渲染,在生产环境仅展示 Auth0 入口(完整示例见 docs/auth/index.md)。
关于 auth0AuthApiRef 的重要迁移说明
需要特别指出:auth0AuthApiRef属于@backstage/core-plugin-api中因"过于通用、缺乏实际契约"而被弃用并最终移除的 Utility API Ref(与oauth2ApiRef、oidcAuthApiRef、samlAuthApiRef一同处理,见 docs/api/deprecations.md)。如果你使用的是较新版本依赖,应按如下方式自定义 API Ref 并基于OAuth2工厂实现:
// 在 packages/app/src/apis.ts(或共享包)中定义 export const acmeAuthApiRef: ApiRef< OAuthApi & OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi > = createApiRef({ id: 'internal.auth.auth0', });// 工厂实现(替换原 auth0AuthApiRef 的用法) createApiFactory({ api: acmeAuthApiRef, deps: { discoveryApi: discoveryApiRef, oauthRequestApi: oauthRequestApiRef, configApi: configApiRef, }, factory: ({ discoveryApi, oauthRequestApi, configApi }) => OAuth2.create({ discoveryApi, oauthRequestApi, provider: { id: 'auth0', title: 'Auth0', icon: () => null, }, defaultScopes: ['openid', 'email', 'profile'], environment: configApi.getOptionalString('auth.environment'), }), });如需在设置页展示该 Provider,还需在packages/app/src/App.tsx的 settings 路由中通过<ProviderSettingsItem>将acmeAuthApiRef传入UserSettingsPage。完整迁移步骤参见 docs/api/deprecations.md。
第五步:配置 Sign-in Resolver 映射用户身份
默认情况下,每个 Backstage 认证 Provider 仅用于访问委托;若要用 Auth0 登录用户,必须显式配置 sign-in 并选择身份解析器(详见 docs/auth/identity-resolver.md)。Auth0 模块内置了以下开箱即用的 Resolver:
emailMatchingUserEntityProfileEmail:将 Auth0 提供的邮箱地址与 Software Catalog 中spec.profile.email匹配的 User 实体对应;未匹配到时抛出NotFoundError;emailLocalPartMatchingUserEntityName:将 Auth0 邮箱地址的本地部分(@之前的局部名)与 Catalog 中name匹配的 User 实体对应;未匹配到时抛出NotFoundError。
多个 Resolver 会按顺序尝试,但只有在抛出
NotFoundError时才会跳过当前解析器、继续尝试下一个。
在app-config.yaml中 Auth0 Provider 配置旁添加signIn.resolvers:
auth: environment: development providers: auth0: development: clientId: ${AUTH_AUTH0_CLIENT_ID} clientSecret: ${AUTH_AUTH0_CLIENT_SECRET} domain: ${AUTH_AUTH0_DOMAIN_ID} signIn: resolvers: - resolver: emailMatchingUserEntityProfileEmail若内置 Resolver 无法满足需求,可参考 docs/auth/identity-resolver.md#building-custom-resolvers 构建自定义 Resolver。需要注意:只应为一个认证 Provider 配置单个 sign-in resolver,多 Provider/多 Resolver 登录会增加账户劫持风险。
从模块实现看,后端在注册 Provider 时通过createOAuthProviderFactory({ authenticator, signInResolverFactories: { ...commonSignInResolvers } })将通用 Resolver 集挂载到auth0Provider 上(见 module.ts),因此上述两个 Resolver 可直接以字符串形式在配置中引用。
进阶原理:Token 刷新、资料缓存与登出
@backstage/plugin-auth-backend-module-auth0-provider的 authenticator.ts 对 OAuth 生命周期做了完整实现:
刷新与缓存(refresh):刷新时先通过PassportHelpers.executeRefreshTokenStrategy使用 Refresh Token 换取新令牌,再解码新id_token中的sub声明作为缓存键(auth0-profile:${sub})。命中缓存则直接复用用户资料,避免重复请求 Auth0 的/userinfo;缓存未命中时通过helper.fetchProfile(accessToken)拉取资料并以 1 分钟 TTL 写入缓存(见 [authenticator.ts](https://link.gitcode.com/i/b9813af99e9b93006315155f236d7c82#L31-L34, L143-L178))。这一行为被 authenticator.test.ts 的测试用例覆盖:同一sub二次刷新时不再调用fetchProfile,而sub变化(不同用户)时会重新拉取资料。若id_token中不含sub,则绕过缓存直接拉取资料。
登出(logout):登出时构造https://{domain}/v2/logout登出端点,附带client_id;若配置了federatedLogout: true,则追加federated参数以同时清除上游 IdP 会话;若请求携带origin头,则作为returnTo参数传回(见 authenticator.ts)。
小结
将 Auth0 接入 Backstage 的完整链路可以概括为四步:① 在 Auth0 控制台创建 Single Page Web Application 并配置回调地址 → ② 在app-config.yaml的auth.providers.auth0下填写clientId/clientSecret/domain(及可选参数),并配置session.secret→ ③ 安装并注册@backstage/plugin-auth-backend-module-auth0-provider后端模块 → ④ 在前端通过SignInPage与 Auth0 认证 API 接入登录,并配置 sign-in resolver 完成用户身份映射。
实际落地时请留意两处易错点:其一,auth0AuthApiRef已从core-plugin-api中移除,需按 docs/api/deprecations.md 的指引自定义 API Ref;其二,session.secret是硬性要求,且应使用足够长的随机字符串。以上配置与代码示例均可在当前仓库的 docs/auth/auth0/provider.md、plugins/auth-backend-module-auth0-provider 及 docs/auth/index.md 中找到对应依据,可直接对照验证。
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考