从零开始的 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
锉刀
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/frame
URL 需要更改为http://localhost:7007/api/auth/my-auth-provider/handler/frame
.
然后,我们需要为提供程序配置环境变量。plugins/auth-backend/src/providers/oidc/provider.ts
我们需要在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 中替换,也可以作为环境变量提供。scope
和prompt
是检查您创建的应用程序注册:
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
andtokenUrl
: Open themetadataUrl
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 specifydefaultScopes
in the provider's factory, basically the same thing. *prompt
: Recommended to useauto
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 变量中需要的不同变量。