Spotify的Backstage,一个开源平台,帮助开发者简化他们的软件基础设施,可以很好地与多种认证方式集成,包括OpenID Connect (OIDC)。本文将重点介绍如何让Backstage使用Keycloak,一个广泛使用的开源身份和访问管理解决方案(IAM),作为其OIDC提供商。
假设你对Backstage和Keycloak有所了解,让我们深入了解一下集成的细节。在这里、如果你需要,快速回顾一下为何及何时使用Backstage。
幕后:使用模板和Terraform进行基础设施自动化:Backstage模板和Terraform在基础设施自动化中的应用 整合后台预设了多种身份验证提供程序,例如GitHub、Okta、OAuth2中继等。虽然设置这些默认解决方案相对简单,但与类似Keycloak的OIDC提供程序集成则需要更多的工作。以下基于原始文档并针对Keycloak进行了调整,简述步骤。我将插入一些注释来引导您设置。
⚠️ 注意: 请注意,这里提供的代码文件是完整的,虽然它们不仅仅包含更改。请检查每个文件顶部的文件名,因为它们对应于 Backstage 仓库 中的代码所在位置。
按照简单的步骤,您可以按照以下步骤启用提供程序:
- 创建一个API参考文档以便识别提供商及其将处理身份验证的API工厂。
// packages/app/src/apis.ts
import {
ScmIntegrationsApi,
scmIntegrationsApiRef,
ScmAuth,
} from '@backstage/integration-react';
import {
AnyApiFactory,
configApiRef,
createApiFactory,
ApiRef,
createApiRef,
OpenIdConnectApi,
ProfileInfoApi,
BackstageIdentityApi,
SessionApi,
discoveryApiRef,
oauthRequestApiRef
} from '@backstage/core-plugin-api';
import { OAuth2 } from '@backstage/core-app-api';
// `ProfileInfoApi & BackstageIdentityApi & SessionApi` 是登录所必需的
export const oidcAuthApiRef: ApiRef<
OpenIdConnectApi & // 处理身份验证的 OICD API
ProfileInfoApi & // 从相关身份提供商请求用户信息资料的资料 API
BackstageIdentityApi & // 处理并关联用户资料与 Backstage 身份的 Backstage 身份 API。
SessionApi // 会话 API,处理用户登录后的会话。
> = createApiRef({
id: 'auth.example.oidc', // 只要不与其他 Api ref ID 冲突就可以是任何东西
});
export const apis: AnyApiFactory[] = [
createApiFactory({
api: oidcAuthApiRef,
deps: {
discoveryApi: discoveryApiRef,
oauthRequestApi: oauthRequestApiRef,
configApi: configApiRef,
},
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
OAuth2.create({
discoveryApi,
oauthRequestApi,
provider: {
id: 'example',
title: '示例身份提供商',
icon: () => null,
},
environment: configApi.getOptionalString('auth.environment'),
defaultScopes: ['openid'],
popupOptions: {
size: {
fullscreen: true,
// 或指定弹出窗口的宽度和高度
//width: 1000,
//height: 1000,
},
},
}),
}),
createApiFactory(
{
api: scmIntegrationsApiRef,
deps: { configApi: configApiRef },
factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi),
}),
ScmAuth.createDefaultApiFactory(),
];
添加身份提供器以便你可以进行身份验证,并添加解析器来处理认证的结果。
// packages/backend/src/plugins/auth.ts
import {
createRouter,
providers,
defaultAuthProviderFactories,
} from '@backstage/plugin-auth-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { DEFAULT_NAMESPACE, stringifyEntityRef } from '@backstage/catalog-model';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
logger: env.logger,
config: env.config,
database: env.database,
discovery: env.discovery,
tokenManager: env.tokenManager,
providerFactories: {
...defaultAuthProviderFactories,
// 注意:在这里添加提供程序和解析函数以从令牌中提取数据
example: providers.oidc.create({
signIn: {
resolver(info, ctx) {
const userRef = stringifyEntityRef({
kind: 'User',
name: info.result.userinfo.sub, // 注意:此信息来自令牌中的userinfo.sub字段
namespace: DEFAULT_NAMESPACE,
});
return ctx.issueToken({
claims: {
sub: userRef, // 用户标识
ent: [userRef], // 用户声明拥有的身份列表
},
});
},
},
}),
},
});
}
- 配置提供方以便能够访问您的第三方认证服务。
# app-config.yaml
...
auth:
### 提供 auth.session.secret 将启用会话
session:
secret: supersecretcookie
# 参考 https://backstage.io/docs/auth/ 了解更多有关身份验证提供程序的详情
environment: development
providers:
example:
development:
# 注意:在此处提供 Keycloak 的 URL
metadataUrl: https://auth.example.com/auth/realms/master/.well-known/openid-configuration
clientId: backstage
clientSecret: supersecretsecret
..
- 在登录页面添加提供商,让用户可以用它登录。
// packages/app/src/App.tsx
import React from 'react';
import { Navigate, Route } from 'react-router-dom';
import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs';
import {
CatalogEntityPage,
CatalogIndexPage,
catalogPlugin,
} from '@backstage/plugin-catalog';
import {
CatalogImportPage,
catalogImportPlugin,
} from '@backstage/plugin-catalog-import';
import {
ScaffolderPage,
scaffolderPlugin,
} from '@backstage/plugin-scaffolder';
import { orgPlugin } from '@backstage/plugin-org';
import { SearchPage } from '@backstage/plugin-search';
import { TechRadarPage } from '@backstage/plugin-tech-radar';
import {
TechDocsIndexPage,
techdocsPlugin,
TechDocsReaderPage,
} from '@backstage/plugin-techdocs';
import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
import { UserSettingsPage } from '@backstage/plugin-user-settings';
import { apis } from './apis';
import { entityPage } from './components/catalog/EntityPage';
import { searchPage } from './components/search/SearchPage';
import { Root } from './components/Root';
import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components';
import { createApp } from '@backstage/app-defaults';
import { AppRouter, FlatRoutes } from '@backstage/core-app-api';
import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
import { RequirePermission } from '@backstage/plugin-permission-react';
import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha';
import { oidcAuthApiRef } from './apis';
import { SignInProviderConfig, SignInPage } from '@backstage/core-components';
// 注意:这里添加示例提供商以显示登录页面的示例
const keycloakProvider: SignInProviderConfig = { // 登录提供商配置
id: 'oidc-auth-provider',
title: 'Keycloak SSO',
message: '使用Keycloak SSO',
apiRef: oidcAuthApiRef,
};
const app = createApp({
components: {
SignInPage: props => (
<SignInPage
{...props}
auto
provider={keycloakProvider}
/>
),
},
apis,
bindRoutes({ bind }) {
// 绑定外部路由
bind(catalogPlugin.externalRoutes, {
createComponent: scaffolderPlugin.routes.root,
viewTechDoc: techdocsPlugin.routes.docRoot,
createFromTemplate: scaffolderPlugin.routes.selectedTemplate,
});
bind(apiDocsPlugin.externalRoutes, {
registerApi: catalogImportPlugin.routes.importPage,
});
bind(scaffolderPlugin.externalRoutes, {
registerComponent: catalogImportPlugin.routes.importPage,
viewTechDoc: techdocsPlugin.routes.docRoot,
});
bind(orgPlugin.externalRoutes, {
catalogIndex: catalogPlugin.routes.catalogIndex,
});
},
});
const routes = (
<FlatRoutes>
<Route path="/" element={<Navigate to="catalog" />} />
<Route path="/catalog" element={<CatalogIndexPage />} />
<Route
path="/catalog/:namespace/:kind/:name"
element={<CatalogEntityPage />}
>
{entityPage}
</Route>
<Route path="/docs" element={<TechDocsIndexPage />} />
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
>
<TechDocsAddons>
<ReportIssue />
</TechDocsAddons>
</Route>
<Route path="/create" element={<ScaffolderPage />} />
<Route path="/api-docs" element={<ApiExplorerPage />} />
<Route
path="/tech-radar"
element={<TechRadarPage width={1500} height={800} />}
/>
<Route
path="/catalog-import"
element={
<RequirePermission permission={catalogEntityCreatePermission}>
<CatalogImportPage />
</RequirePermission>
}
/>
<Route path="/search" element={<SearchPage />}>
{searchPage}
</Route>
<Route path="/settings" element={<UserSettingsPage />} />
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
</FlatRoutes>
);
// 导出默认应用根组件(包含所有路由和组件)
export default app.createRoot(
<>
<AlertDisplay />
<OAuthRequestDialog />
<AppRouter>
<Root>{routes}</Root>
</AppRouter>
</>,
);
因此,结尾
最后,将Keycloak作为OIDC提供者与Spotify的Backstage结合使用,为开发人员提供了一个坚实的框架,用于在其软件生态系统中管理用户的认证和授权。尽管Backstage从一开始就支持多种认证方式,但要让两者无缝对接则需要一些额外的步骤。这篇文章详细介绍了必要的步骤,使开发人员能够利用Keycloak强大的身份和访问管理工具,同时享受Backstage提供的高效开发体验。
想了解最新的云技术,快来订阅我的每周简报,Cloud Chirp,快来订阅吧!🚀
共同學習,寫下你的評論
評論加載中...
作者其他優質文章