在Backstage中集成Microsoft Azure进行身份验证并实现伪SSO

在Backstage中集成Microsoft Azure进行身份验证并实现伪SSO

Zave Genshin Impact

注意
本文档基于 Backstage 1.32.0 版本编写,并采用自 1.24.0 版本以来官方推荐使用的新后端系统(New BackEnd System)。Backstage 仍处于不断更新发展中,本文下述步骤可能将不适用于后续更新版本。

一、开发前准备

  1. 确认 Backstage 服务能够访问外部网络或与公司内部网络互通
  2. 开发参考文档:

二、在 Microsoft Azure 中创建 Backstage 对应的应用程序

(1)新建应用程序

(2)填写应用程序信息

这里的 URL,平台选择 Web,填写的 url 值为:

1
https://backstage-api.xxxxxx.com.cn/api/auth/microsoft/handler/frame

(3)点击注册

保存好应用的以下几项信息:

  1. clientId
  2. clientSecret
  3. tenantId
  4. domainHint

三、在前端搭建登录接口

(1)在 packages/app/src 文件夹下的 App.tsx 文件中添加如下内容:

编辑 packages/app/src/App.tsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import { useApi, microsoftAuthApiRef, identityApiRef } from '@backstage/core-plugin-api';

const app = createApp({
// others
components: {
SignInPage: (props) => {
const microsoftAuthApi = useApi(microsoftAuthApiRef);
const IdentityApi = useApi(identityApiRef);

useEffect(() => {
const isLogin = async () => {
try {
const bkstCredentials = await IdentityApi.getBackstageIdentity();
if (bkstCredentials === null) await microsoftAuthApi.signOut();
} catch (error) {
console.log('Failed to fetch credentials:', error);
}
};
}, []);

return (
<SignInPage
{...props}
auto
providers={[
'guest',
{
id: 'microsoft-auth-provider',
title: 'Azure Active Directory', // any title you want
message: 'Sign in using Azure AD', // any msg you want
apiRef: microsoftAuthApiRef,
},
]}
/>
);
},
},
});

其中,id 需要填写为一个唯一值,title 和 message 的值将显示在前端登录接口面板上,apiRef 表示将会使用 Backstage 中封装好的这个 api 接口。

return 之前的部分则是登出逻辑,而不是一直保留。若不确保在使用 Azure 登录后、登出账号时,在 Backstage 中绑定的 Azure 账号信息也能一并注销,那么在登出后再使用访客账号登录,无论是操作还是访问都将使用到刚才留存的 Azure 账号信息,这将会显著威胁系统安全。

此时,重启系统,我们就会看到在登录界面出现了新的登录可选项:

但此时点击其 SIGN IN 按钮,则会出现 502 信息,是因为我们还需要进行后端的配置,以确保我们能够正确地对接 Azure 服务器以及正确地重定向。

四、编写后端配置

(1)安装依赖

从项目的根目录开始:

1
2
yarn --cwd packages/app add @backstage/plugin-auth-backend-module-microsoft-provider
yarn add node-cache

(2)创建全局存储模块

在 packages 文件夹下新建 global 文件夹,并在其中新建 node-cache.ts 文件。接着向文件中编写如下内容:

packages/global/node-cache.ts:

1
2
3
4
5
6
import NodeCache from 'node-cache';

// 创建一个 node-cache 实例
const myCache = new NodeCache();

export default myCache;

此存储模块的作用在于,能够在每个用户使用 Azure 方式登录时,存储其登录账号对应的 refresh_token。我们需要利用 refresh_token 在后续各项集成模块的工作步骤中去获取到 id_token,借此实现伪 SSO(也就是免掉了向集成的各个工具提供账密认证的工作,只需从 Backstage 登录一次,就能实现免登录访问、操作、处理集成的其他工具)。

(3)编写自定义身份验证模块

需要在 backend/src/plugins 文件夹中新建文件 azureAuth.ts,并在其中编写如下内容:

packages/backend/src/plugins/azureAuth.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import { createBackendModule } from '@backstage/backend-plugin-api';
import { microsoftAuthenticator } from '@backstage/plugin-auth-backend-module-microsoft-provider';
import { stringifyEntityRef, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
import {
authProvidersExtensionPoint,
createOAuthProviderFactory,
} from '@backstage/plugin-auth-node';
import myCache from '../../../global/node-cache';

const customAzureAuth = createBackendModule({
// This ID must be exactly "auth" because that's the plugin it targets
pluginId: 'auth',
// This ID must be unique, but can be anything
moduleId: 'custom-auth-provider',
register(reg) {
reg.registerInit({
deps: { providers: authProvidersExtensionPoint },
async init({ providers }) {
providers.registerProvider({
// This ID must match the actual provider config, e.g. addressing
// auth.providers.github means that this must be "github".
providerId: 'microsoft',
// Use createProxyAuthProviderFactory instead if it's one of the proxy
// based providers rather than an OAuth based one
factory: createOAuthProviderFactory({
authenticator: microsoftAuthenticator,
async signInResolver(info, ctx) {
if (!info.profile.email) {
throw new Error(
'Login failed, user profile does not contain an email',
);
}

// Split the email into the local part and the domain.
const [localPart, domain] = info.profile.email.split('@');

// Next we verify the email domain. It is recommended to include this
// kind of check if you don't look up the user in an external service.
if (domain !== 'xxxxxx.com.cn') {
throw new Error(
`Login failed, this email ${info.profile.email} does not belong to the expected domain`,
);
}

// By using `stringifyEntityRef` we ensure that the reference is formatted correctly
// 使用 idToken 可以实现登录操作
// info.result.session.idToken
const userEntity = stringifyEntityRef({
kind: 'User',
name: localPart,
namespace: DEFAULT_NAMESPACE,
});

// 将登录用户的 refreshtoken 存入 cache 中,永久保存
const name = localPart.toLocaleLowerCase();
myCache.set(
`${name}_refreshToken`,
info.result.session.refreshToken,
0,
);
console.log('refreshToken:', info.result.session.refreshToken);

return ctx.issueToken({
claims: {
sub: userEntity,
ent: [userEntity],
},
});

/** *******************************************************************
* Custom resolver code goes here, see farther down in this article! *
* "info" is the sign in result from the upstream (github here), and *
* "ctx" contains useful utilities for token issuance etc. *
******************************************************************* **/
},
}),
});
},
});
},
});

export default customAzureAuth;

根据上面的例子,我们通过 Backstage 内置的 Microsoft AuthApi 执行相应的验证操作。在 signInResolver 函数中我们新增了一些额外的验证处理,比如 info 参数中存储了在前台点击 SIGN IN 按钮时唤起的 Microsoft 登录窗口中填写的邮箱,我们可以预先验证邮箱是否属于本公司或符合预期条件,如果不符合就会弹出错误提示。若验证通过,则会将用户名的 refresh_token 存储到 node-cache 中,为确保每位用户的 refresh_token 的独立性,此处存储的 key 命名由 邮箱用户名 + refreshToken 组成。

(4)向后端导入身份验证模块

在 backend 文件夹下的 index.ts 文件中添加如下内容:

packages/backend/src/index.ts:

1
2
// ... other plugins
backend.add(import('./plugins/azureAuth'));

(5)编写配置

在 app-config.yaml 文件中的 auth 部分编写如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
auth:
# see https://backstage.io/docs/auth/ to learn about auth providers
enviroment: development
providers:
# See https://backstage.io/docs/auth/guest/provider
guest: {}
microsoft:
development:
clientId: 'xxxx'
clientSecret: 'xxxx'
tenantId: 'xxxx'
domainHint: 'xxxx'
callbackUrl: https://backstage-api.xxxxxx.com.cn/api/auth/microsoft/handler/frame

其中的 clientId、clientSecret、tenantId、domainHint 填写的是之前步骤中保存的信息。callbackUrl 的值填写的是之前创建应用程序时填写的回调地址。

上述步骤全部完成后,重新启动系统,即可使用 Azure 进行登录。登录成功后界面显示如下:

至此,Azure 登录集成以及伪 SSO 实现就基本完成了。

  • 标题: 在Backstage中集成Microsoft Azure进行身份验证并实现伪SSO
  • 作者: Zave
  • 创建于 : 2026-10-09 12:00:00
  • 更新于 : 2026-10-09 12:00:00
  • 链接: https://zavex.cn/post/55104.html
  • 版权声明: 版权所有 © Zave,禁止转载。
评论