Skip to main content

从零开始的 OIDC 提供商

本节将介绍如何从头开始使用 OIDC 提供程序,同样的步骤也适用于自定义提供程序。 请注意,这些步骤是用于使用提供程序,而不是如何实现提供程序,Backstage 建议创建特定于 IDP 的自定义提供程序,因此我们将使用一个azureOIDC在本示例中,您可以将任何这些引用改成您的提供商名称。

摘要

要添加默认未启用的提供程序(如 OIDC),我们需要遵循一些步骤,我们假设您已经有一个登录页面,我们将在其中添加提供程序,以便用户可以通过提供程序登录。 以下是启用提供程序的简单步骤:

  • 添加或重复使用一个验证提供程序,以便进行验证 * 添加或重复使用一个解析器,以便处理验证结果 * 配置提供程序,以便访问第三方验证解决方案 * 将提供程序添加到登录页面,以便用户使用它登录。

接下来,我们将详细介绍每个步骤。

应用程序接口参考

为方便起见,我们提供了一个应用程序接口参考。依赖注入检查实用 API进行详细解释。

在本 OIDC 示例中,我们将直接在packages/app/src/apis.ts任何位置都可以,只要能导入到 API 工厂所在的位置,并且便于应用程序的其他部分访问,以便任何软件包和插件都能在必要时注入 API 实例。

例如,当您使用安装了 NPM 或其他库的库中的认证提供程序时,您需要从库中导入 API ref。

export const azureOIDCAuthApiRef: ApiRef<
OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
> = createApiRef({
id: 'auth.my-custom-provider',
});

请注意以下几点:ID 可以是任何你想要的名称,只要不与其他引用冲突即可,backstage 建议使用引用你的自定义提供商的自定义名称,例如,我们与 Azure 一起使用 OIDC 协议,因此我们可以使用类似的名称auth.azure.oidc也是如此。

此外,我们还将导出此引用以及typings因此,我们需要能够在应用程序中的任何地方导入此引用,而typings将告诉 typescript 我们在注入 API 时从 DI 获取的实例。 在本例中,我们定义了一个用于身份验证的 API,因此我们告诉 TS 该实例符合 4 个 API 接口:

  • OICD API,用于处理身份验证。 * Profile API,用于向相关认证提供商请求用户配置文件信息。 * Backstage identity API,用于处理用户配置文件并将其与Backstage身份关联。 * Session API,用于处理用户登录时的会话。

###应用程序接口工厂

工厂是一个函数,它可以接受一些参数或依赖关系,并返回一个实例,在我们的例子中,它将是一个请求一些Backstage API 并使用它们创建 OIDC API 提供商实例的函数。

请注意,只有当您在应用程序的其他地方使用上面定义的 API ref 请求 DI 给您提供 OIDC 提供程序的实例时,该函数才会运行(创建实例),而且 DI 只会在第一次运行该函数,从那以后,任何其他 DI 注入都只会接收到第一次创建的相同实例,基本上,该实例是由 DI 库缓存的,是一个单例。

让我们将 OIDC API 工厂添加到packages/app/src/apis.ts锉刀

packages/app/src/apis.ts
import { OAuth2 } from '@backstage/core-app-api';

export const apis: AnyApiFactory[] = [
createApiFactory({
api: azureOIDCAuthApiRef,
deps: {
discoveryApi: discoveryApiRef,
oauthRequestApi: oauthRequestApiRef,
configApi: configApiRef,
},
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
OAuth2.create({
discoveryApi,
oauthRequestApi,
provider: {
id: 'my-auth-provider',
title: 'My custom auth provider',
icon: () => null,
},
environment: configApi.getOptionalString('auth.environment'),
defaultScopes: ['openid', 'profile', 'email'],
popupOptions: {
// optional, used to customize login in popup size
size: {
fullscreen: true,
},
/**
* or specify popup width and height
* size: {
width: 1000,
height: 1000,
}
*/
},
}),
}),
// ..
];

请注意,我们正在导入OAuth2@backstage/core-app-api此外,我们还使用了my-auth-provider身份证说明OAuth2以使用我们将在下一节定义的认证提供程序,并添加了默认作用域以请求 ID、配置文件、电子邮件和用户读取权限。

验证提供程序

Auth 提供商负责与第三方服务进行身份验证,并将凭据反馈给我们,在此您可以选择使用哪种协议,无论是 Auth0、OAuth2、OIDC、SAML 还是第三方 IDP 提供商支持的任何其他协议。

在本例中,我们将使用 OIDC,将一个工厂传递给providerFactories对象,该 ID 必须与 Auth 提供商的id中的 yaml 配置提供者密钥。auth.providers和回调 URI 提供程序段(您必须配置 IDP 以正确处理回调 URI)。

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,
'my-auth-provider': providers.oidc.create({}),
},
// ..
})

调解员

解析器用于将第三方(本例中为azure IDP 提供商)的用户身份映射到Backstage用户身份。身份解析器页面,介绍了如何编写自定义解析器以及如何链接Backstage内置的解析器。

默认的 OIDC 提供商不支持 SignIn,我们需要通过为 SignIn 请求添加解析器来添加这种支持。

OIDC 提供商不提供任何内置解析器,因此我们需要定义自己的解析器:

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,
'my-auth-provider': providers.oidc.create({
signIn: {
resolver(info, ctx) {
const userRef = stringifyEntityRef({
kind: 'User',
name: info.result.userinfo.sub,
namespace: DEFAULT_NAMESPACE,
});
return ctx.issueToken({
claims: {
sub: userRef, // The user's own identity
ent: [userRef], // A list of identities that the user claims ownership through
},
});
},
},
}),
},
// ..
})

配置

由于我们使用的是自定义 OIDC Auth Provider,因此需要根据所使用的提供程序添加配置,在本例中是根据 OIDC 协议添加配置(请记住,第三方必须支持该协议)。

在本例中,我们将通过以下方式配置 OIDCmy-auth-provider为此,我们需要创建应用程序注册在 Azure 控制台中,唯一的区别是http://localhost:7007/api/auth/microsoft/handler/frameURL 需要更改为http://localhost:7007/api/auth/my-auth-provider/handler/frame.

然后,我们需要为提供程序配置环境变量。plugins/auth-backend/src/providers/oidc/provider.ts我们需要在app-config.yaml:

app-config.yaml
auth:
environment: development
### Providing an auth.session.secret will enable session support in the auth-backend
session:
secret: ${SESSION_SECRET}
providers:
my-auth-provider:
development:
metadataUrl: https://example.com/.well-known/openid-configuration
clientId: ${AUTH_MY_CLIENT_ID}
clientSecret: ${AUTH_MY_CLIENT_SECRET}

任何括在${}可以直接在 yaml 中替换,也可以作为环境变量提供。scopeprompt是检查您创建的应用程序注册:

  • clientId: Grab from the Overview page. * clientSecret: Can only be seen when creating the secret, if you lose it you'll need a new secret. * metadataUrl: In Overview > Endpoints tab, grab OpenID Connect metadata document URL. * authorizationUrl and tokenUrl: Open the metadataUrl in a browser, that json will hold these 2 urls somewhere in there. * tokenEndpointAuthMethod: Don't define it, use the default unless you know what it does. * tokenSignedResponseAlg: Don't define it, use the default unless you know what it does. * scope: Only used if we didn't specify defaultScopes in the provider's factory, basically the same thing. * prompt: Recommended to use auto so the browser will request login to the IDP if the user has no active session.

请注意,目前对该 yaml 文件的任何更改都需要重启应用程序,而且您还需要将session.secret部分使用 OIDC(其他一些提供商可能也需要)来支持用户会话。

登录提供者

最后一步是将提供程序添加到SignInPage以便用户可以登录新的提供商,请按照以下步骤操作登录配置文档,这里就是导入和使用我们之前定义的 API 引用的地方。

注意

这些步骤适用于大多数(如果不是全部)提供程序,包括自定义提供程序,不同提供程序之间的主要区别在于 API 工厂的内容、Auth 提供程序工厂中的代码、解析器,以及每个提供程序在 YAML 配置或 env 变量中需要的不同变量。