Skip to main content

贡献新供应商

注意:本文档的主要受众是希望为新的身份验证提供程序添加支持的主 > Backstage 项目的贡献者。 > 虽然你可以按照它来实现自己的自定义提供程序,但它比使用我们的内置提供程序要高级得多。

验证如何进行?

Backstage应用程序可以使用各种外部身份验证提供程序进行身份验证。 外部提供程序使用一个AuthProviderRouteHandlers该接口由四个方法组成,每个方法都由一个端点托管(默认情况下)/api/auth/[provider]/method其中method执行如下操作

/auth/[provider]/start -> Initiate a login from the web page
/auth/[provider]/handler/frame -> Handle a finished authentication operation
/auth/[provider]/refresh -> Refresh the validity of a login
/auth/[provider]/logout -> Log out a logged-in user

流程如下

1.用户尝试登录。 2.弹出窗口打开,指向 "auth "端点。 该端点进行初始准备,然后将用户重新定向到外部验证器,仍在弹出窗口内。 3.验证器验证用户,并将验证结果(成功或失败)返回给包装器端点("处理程序/框架")。 4. "处理程序/框架 "渲染的网页将向打开弹出窗口的网页发出适当的响应,弹出窗口关闭。 5.用户点击用户界面退出,网页请求注销用户。

实施自己的 Auth 封装器

任何认证包装器的核心接口都是AuthProviderRouteHandlers该接口有四种方法,与最初章节中描述的 API 相对应。 任何 auth 封装器都必须实现该接口。

启动登录时,前台会弹出一个窗口,允许用户启动登录。 登录请求会发送到/start端点,该端点由start方法。

start方法会重定向到外部认证提供程序,后者会对请求进行身份验证,并将请求重定向到/handler/frame端点,由frameHandler方法。

frameHandler会返回一个 HTML 响应,其中包含一个脚本,该脚本会执行postMessage前台窗口,其中包含请求结果。WebMessageResponse类型是由postMessage到前台。

ApostMessageResponse效用函数封装了生成一个postMessage响应,以确保成功处理 CORS。express.Response, aWebMessageResponse和前台的 URL (appOrigin)作为参数,并返回包含脚本和信息的 HTML 页面。

有一个OAuth2基于身份验证的提供商、OAuthAdapter该类实现了AuthProviderRouteHandlers接口,而是要求您实现OAuthHandlers这要容易得多。

验证环境分离

的概念。env是身份验证后端工作方式的核心,它使用一个env查询参数,以确定运行应用程序的环境 (development,staging,production每个运行时可同时支持多个环境,并根据env参数。

OAuthEnvironmentHandler是一个OAuthHandlersAuthProviderRouteHandlers接口,同时支持多个envs.

要实例化 OAuth 提供程序(相同但用于不同环境),请使用OAuthEnvironmentHandler.mapConfig它是一个迭代配置对象的辅助工具,配置对象是环境到配置的映射。 请参阅现有的 OAuth 提供程序,了解如何使用它。

配置如下

development:
clientId: abc
clientSecret: secret
production:
clientId: xyz
clientSecret: supersecret

OAuthEnvironmentHandler.mapConfig(config, envConfig => ...)调用将按顶层developmentproduction键,并以envConfig.

为方便起见AuthProviderFactory是一个必须实现的工厂函数,它可以生成一个AuthProviderRouteHandlers为某个提供商服务。

所有受支持的提供商都提供AuthProviderFactory返回一个OAuthEnvironmentHandler能够处理多个环境的身份验证。

Passport

我们选择了Passport作为我们的身份验证平台,因为它拥有一整套受支持的身份验证功能策略.

如何添加新策略提供者

快速指南

1.安装基于 passport-js 的提供程序包。

2.为提供程序创建新的文件夹结构。

3.实施提供商,必要时扩展合适的框架。

4.将提供程序添加到后端。

安装依赖项:

cd plugins/auth-backend
yarn add passport-provider-a
yarn add @types/passport-provider-a

创建实施

按照以下文件结构新建一个文件夹,名称为提供程序:

plugins/auth-backend/src/providers/providerA
├── index.ts
└── provider.ts

**plugins/auth-backend/src/providers/providerA/provider.ts**定义了为所选框架实现处理程序的提供程序类。

添加基于 OAuth 的提供程序

如果我们要添加一个OAuth我们将实施OAuthHandlers通过实现该接口,我们可以使用OAuthProviderlib/oauth这意味着我们不需要实现完整的AuthProviderRouteHandlers接口,否则提供商需要实施该接口。

提供程序类将提供程序的选项作为类参数。 它还导入了Strategy从 passport 包中取出。

import { Strategy as ProviderAStrategy } from 'passport-provider-a';

export type ProviderAProviderOptions = OAuthProviderOptions & {
// extra options here
}

export class ProviderAAuthProvider implements OAuthHandlers {
private readonly _strategy: ProviderAStrategy;

constructor(options: ProviderAProviderOptions) {
this._strategy = new ProviderAStrategy(
{
clientID: options.clientId,
clientSecret: options.clientSecret,
callbackURL: options.callbackUrl,
passReqToCallback: false,
response_type: 'code',
/// ... etc
}
verifyFunction, // See the "Verify Callback" section
);
}

async start() {}
async handler() {}
}

添加非基于 OAuth 的提供商

OAuth提供商可以实施AuthProviderRouteHandlers而不是

type ProviderAOptions = {
// ...
};

export class ProviderAAuthProvider implements AuthProviderRouteHandlers {
private readonly _strategy: ProviderAStrategy;

constructor(options: ProviderAOptions) {
this._strategy = new ProviderAStrategy(
{
// ...
},
verifyFunction, // See the "Verify Callback" section
);
}

async start() {}
async frameHandler() {}
async logout() {}
async refresh() {} // If supported
}

集成包装器

每个提供程序都会导出一个对象,提供创建新提供程序实例的方法,以及预定义登录解析器等相关实用程序。

创建对象时使用createAuthProviderIntegration最重要的部分是create方法,作为我们提供程序的工厂函数。

工厂应返回AuthProviderFactory的实现。AuthProviderRouteHandlers.

工厂是决定从静态配置例如,OAuth 提供程序使用OAuthEnvironmentHandler以允许多个不同的配置,每个环境一个,看起来像这样;

export const okta = createAuthProviderIntegration({
create(options?: {
/**
* The profile transformation function used to verify and convert the auth response
* into the profile that will be presented to the user.
*/
authHandler?: AuthHandler<OAuthResult>;

/**
* Configure sign-in for this provider, without it the provider can not be used to sign users in.
*/
signIn?: {
/**
* Maps an auth result to a Backstage identity for the user.
*/
resolver: SignInResolver<OAuthResult>;
};
}) {
return ({ providerId, globalConfig, config, resolverContext }) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
// read options from config
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');

// Use provided auth handler, or create a default one
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
? options.authHandler
: async ({ fullProfile, params }) => ({
profile: makeProfileInfo(fullProfile, params.id_token),
});

// instantiate our OAuthHandlers implementation
const provider = new OktaAuthProvider({
audience,
clientId,
clientSecret,
callbackUrl,
authHandler,
signInResolver: options?.signIn?.resolver,
resolverContext,
});

// Wrap the OAuthHandlers with OAuthProvider, which implements AuthProviderRouteHandlers
return OAuthProvider.fromConfig(globalConfig, provider, {
providerId,
tokenIssuer,
});
});
},
resolvers: {
/**
* Looks up the user by matching their email local part to the entity name.
*/
emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver,

// ... additional predefined resolvers
},
});

不同环境的目的是让单个 auth 后端为多个不同的前端环境(如本地开发、暂存和生产)提供身份验证服务。

验证回调

策略需要所谓的验证回调。 验证 > 回调的目的是找到拥有一组凭据的用户。 当 > Passport 验证一个请求时,它会解析 > 请求中包含的凭据,然后调用验证回调,并将这些凭据作为 > 参数 [...] 。 如果凭据有效,验证回调会调用 > done,向 Passport 提供验证的用户。 > > 如果凭据无效(例如,如果密码不正确), > done 应调用 false 而不是用户,以表示 > 验证失败。 > > http://www.passportjs.org/docs/configure/

**plugins/auth-backend/src/providers/providerA/index.ts**只是将工厂函数重新导出,用于将提供程序连接到后端。

export { createProviderAProvider } from './provider';

连接到后端

**plugins/auth-backend/src/providers/factories.ts**当auth-backend您需要从提供程序导入工厂函数,并将其添加到工厂中:

import { createProviderAProvider } from './providerA';

const factories: { [providerId: string]: AuthProviderFactory } = {
providerA: createProviderAProvider,
};

通过这样做auth-backend会自动添加这些端点:

router.get('/auth/providerA/start');
router.get('/auth/providerA/handler/frame');
router.post('/auth/providerA/handler/frame');
router.post('/auth/providerA/logout');
router.get('/auth/providerA/refresh'); // if supported
router.post('/auth/providerA/refresh'); // if supported

可以看到,每个端点的前缀都是/auth及其提供商名称。

测试新的提供商

您可以curl -i localhost:7007/api/auth/providerA/start并应提供302Location将该标头中的 URL 粘贴到网络浏览器中,就可以触发授权流程了。