使用 Azure AD B2C 在示例单页应用程序中配置身份验证
本文使用一个示例 JavaScript 单页应用程序 (SPA),说明如何向 SPA 添加 Azure Active Directory B2C (Azure AD B2C) 身份验证。
概述
OpenID Connect (OIDC) 是在 OAuth 2.0 上构建的身份验证协议。 你可以使用它安全地将用户登录到应用程序。 此 SPA 示例使用了 MSAL.js 和 OIDC PKCE 流。 MSAL.js 是 Microsoft 提供的库,可简化向 SPA 添加身份验证和授权支持的过程。
登录流
登录流涉及以下步骤:
- 用户前往 Web 应用,并选择“登录”。
- 应用发起身份验证请求,并将用户重定向到 Azure AD B2C。
- 用户注册或登录并重置密码。 或者,用户也可以使用社交帐户登录。
- 用户成功登录后,Azure AD B2C 向应用返回一个授权代码。
- 单页应用程序验证 ID 令牌,读取声明,进而允许用户调用受保护的资源和 API。
应用注册概述
若要让应用能够使用 Azure AD B2C 登录并调用 Web API,你需要在 Azure AD B2C 目录中注册两个应用程序。
Web 应用注册能让你的应用使用 Azure AD B2C 登录。 在注册过程中,你需要指定重定向 URI。 重定向 URI 是用户在使用 Azure AD B2C 完成身份验证后,Azure AD B2C 将用户重定向到的终结点。 此应用注册过程会生成应用程序 ID(也称为“客户端 ID”),将其作为该应用的唯一标识。
Web API 注册可使应用调用安全的 Web API。 注册内容包括 Web API 的作用域。 作用域提供了一种方法来管理受保护资源(例如你的 Web API)的访问权限。 你可以向 Web API 的作用域授予 Web 应用访问权限。 在请求访问令牌时,你的应用需要在请求的作用域参数中指定所需的访问权限。
下图演示了应用体系结构和注册:
调用 Web API
身份验证完成后,用户将与应用交互,应用会调用受保护的 Web API。 Web API 使用持有者令牌身份验证。 持有者令牌是应用从 Azure AD B2C 获取的访问令牌。 应用在 HTTPS 请求的授权标头中传递令牌。
Authorization: Bearer <access token>
如果访问令牌范围与 Web API 范围不一致,身份验证库将获取具有正确范围的新访问令牌。
注销流
注销流程涉及以下步骤:
- 用户从应用中注销。
- 应用清除其会话对象,并且身份验证库清除其令牌缓存。
- 应用将用户定向到 Azure AD B2C 注销终结点,以终止 Azure AD B2C 会话。
- 用户被重定向回应用。
先决条件
运行以下内容的计算机:
- Visual Studio Code 或其他代码编辑器。
- Node.js 运行时
步骤 1:配置用户流
当用户尝试登录你的应用时,该应用会通过用户流向授权终结点发起身份验证请求。 用户流定义并控制用户体验。 用户完成用户流后,Azure AD B2C 会生成一个令牌,然后将用户重定向回应用程序。
请创建用户流或自定义策略(如果你尚未这样做)。 重复这些步骤以按如下所述创建三个单独的用户流:
- 组合的“登录和注册”用户流,例如
susi
。 此用户流还支持“忘记密码”体验。 - “个人资料编辑”用户流,例如
edit_profile
。 - “密码重置”用户流,例如
reset_password
。
Azure AD B2C 将在用户流名称前面追加 B2C_1_
。 例如,susi
重命名为 B2C_1_susi
。
步骤 2:注册 SPA 和 API
在此步骤中,你将创建 SPA 和 Web API 应用程序注册,并指定 Web API 的作用域。
步骤 2.1:注册 Web API 应用程序
请按照以下步骤创建 Web API 应用注册(应用 ID: 2):
登录 Azure 门户。
确保正在使用的目录包含 Azure AD B2C 租户。 在门户工具栏中选择“目录 + 订阅”图标。
在“门户设置 | 目录+订阅”页上的“目录名称”列表中找到你的 Azure AD B2C 目录,然后选择“切换”。
在 Azure 门户中,搜索并选择“Azure AD B2C”。
选择“应用注册”,然后选择“新建注册” 。
对于名称,请输入应用程序的名称(例如 my-api1) 。 保留“重定向 URI”和“支持的帐户类型”的默认值。
选择“注册”。
完成应用注册后,选择“概述”。
记录“应用程序(客户端) ID”值,以便在稍后配置 Web 应用程序时使用。
步骤 2.2:配置作用域
选择所创建的 my-api1 应用程序(应用 ID:2)以打开其“概述”页面 。
在“管理”下,选择“公开 API” 。
选择“应用程序 ID URI”旁边的“设置”链接。 将默认值 (GUID) 替换为一个唯一名称(例如“tasks-api”),然后选择“保存”。
Web 应用在请求 Web API 的访问令牌时,应将此 URI 添加为你为 API 定义的每个范围的前缀。
在“此 API 定义的范围”下选择“添加范围”。
若要创建一个用于定义对 API 的读取访问权限的范围,请执行以下操作:
- 对于“范围名称”,输入“tasks.read” 。
- 对于“管理员同意显示名称”,输入“对任务 API 的读取访问权限” 。
- 对于“管理员同意说明”,输入“允许对任务 API 进行读取访问” 。
选择“添加作用域”。
选择“添加范围”,然后添加一个用于定义对 API 的写入访问权限的范围:
- 对于“范围名称”,输入“tasks.write” 。
- 对于“管理员同意显示名称”,“对任务 API 的写入访问权限” 。
- 对于“管理员同意说明”,输入“允许对任务 API 进行写入访问” 。
选择“添加作用域”。
步骤 2.3:注册 SPA
要创建 SPA 注册,请执行以下步骤:
- 登录 Azure 门户。
- 如果有权访问多个租户,请选择顶部菜单中的“设置”图标,切换到“目录 + 订阅”菜单中的 Azure AD B2C 租户。
- 搜索并选择“Azure AD B2C”。
- 选择“应用注册”,然后选择“新建注册” 。
- 输入应用程序的名称(例如 MyApp)。
- 在“支持的帐户类型”下,选择“任何标识提供者或组织目录中的帐户(用于通过用户流对用户进行身份验证)” 。
- 在“重定向 URI”下,选择“单页应用程序 (SPA)”,然后在 URL 框中输入
http://localhost:6420
。 - 在“权限”下,选中“授予对 OpenID 和脱机访问权限的管理员许可”复选框。
- 选择“注册” 。
记录“应用程序(客户端)ID”,以便在稍后配置 Web 应用程序时使用。
步骤 2.4:启用隐式授权流
可以启用隐式授权流有两个原因,当使用 MSAL.js 版本 1.3 或更早版本时,或者当使用应用注册来测试用户流以进行测试时。
使用以下步骤为应用启用隐式授权流:
选择创建的应用注册。
在“管理”下,选择“身份验证”。
在“隐式授权和混合流”下,同时选中“访问令牌(用于隐式流)”和“ID 令牌(用于隐式流和混合流)”复选框。
选择“保存”。
注意
如果你的应用使用 MSAL.js 2.0 或更高版本,请不要启用隐式授权流,因为 MSAL.js 2.0 + 支持 OAuth 2.0 授权代码流(使用 PKCE)。 如果启用隐式授权来测试用户流,请确保在将应用部署到生产环境之前禁用隐式授权流设置。
步骤 2.5:授予权限
若要向应用(应用 ID:1)授予权限,请执行以下步骤:
选择“应用注册”,然后选择你所创建的应用(应用 ID:1) 。
在“管理”下选择“API 权限”。
在“已配置权限”下,选择“添加权限”。
选择“我的 API”选项卡。
选择应授予 Web 应用程序访问权限的 API(应用 ID:2)。 例如,输入“my-api1”。
在“权限”下展开“任务”,然后选择之前定义的范围(例如,tasks.read 和 tasks.write )。
选择“添加权限”。
选择“向<租户名称>授予管理员许可”。
选择 “是” 。
选择“刷新”,然后验证两个范围的“状态”下是否均显示“已授予...” 。
从“配置权限”列表中,选择范围,然后复制范围全名。
步骤 3:获取 SPA 示例代码
此示例演示了单页应用程序如何使用 Azure AD B2C 来实现用户注册和登录。 然后,该应用将获取访问令牌并调用受保护的 Web API。
若要获取 SPA 示例代码,可以执行以下操作之一:
通过运行以下命令,从 GitHub 克隆该示例代码:
git clone https://github.com/Azure-Samples/ms-identity-b2c-javascript-spa.git
步骤 3.1:更新 SPA 示例
获取 SPA 示例后,请使用 Azure AD B2C 和 Web API 值更新代码。 在示例文件夹中,从 App
文件夹下,打开下表所列的 JavaScript 文件,然后使用相应的值更新这些文件。
文件 | 密钥 | 值 |
---|---|---|
authConfig.js | clientId | 步骤 2.3 中的 SPA ID。 |
policies.js | 姓名 | 在步骤 1 中创建的用户流或自定义策略。 |
policies.js | authorities | Azure AD B2C 用户流或自定义策略颁发机构,例如 https://<your-tenant-name>.b2clogin.cn/<your-tenant-name>.partner.onmschina.cn/<your-sign-in-sign-up-policy> 。 将 your-sign-in-sign-up-policy 替换为在步骤 1 中创建的用户流或自定义策略 |
policies.js | authorityDomain | Azure AD B2C 颁发机构域,例如 <your-tenant-name>.b2clogin.cn 。 |
apiConfig.js | b2cScopes | 你在步骤 2.2 中创建的 Web API 作用域(例如 b2cScopes: ["https://<your-tenant-name>.partner.onmschina.cn/tasks-api/tasks.read"] )。 |
apiConfig.js | webApi | Web API 的 URL,即 http://localhost:5000/hello 。 |
最终的代码应类似于以下示例:
authConfig.js:
const msalConfig = {
auth: {
clientId: "<your-MyApp-application-ID>", // This is the ONLY mandatory field; everything else is optional.
authority: b2cPolicies.authorities.signUpSignIn.authority, // Choose sign-up/sign-in user-flow as your default.
knownAuthorities: [b2cPolicies.authorityDomain], // You must identify your tenant's domain as a known authority.
redirectUri: "http://localhost:6420", // You must register this URI on Azure Portal/App Registration. Defaults to "window.location.href".
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false,
},
system: {
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return;
}
switch (level) {
case msal.LogLevel.Error:
console.error(message);
return;
case msal.LogLevel.Info:
console.info(message);
return;
case msal.LogLevel.Verbose:
console.debug(message);
return;
case msal.LogLevel.Warning:
console.warn(message);
return;
}
}
}
}
};
};
const loginRequest = {
scopes: ["openid", ...apiConfig.b2cScopes],
};
const tokenRequest = {
scopes: [...apiConfig.b2cScopes], // e.g. ["https://fabrikamb2c.partner.onmschina.cn/helloapi/demo.read"]
forceRefresh: false // Set this to "true" to skip a cached token and go to the server to get a new token
};
policies.js:
const b2cPolicies = {
names: {
signUpSignIn: "b2c_1_susi",
forgotPassword: "b2c_1_reset",
editProfile: "b2c_1_edit_profile"
},
authorities: {
signUpSignIn: {
authority: "https://your-tenant-name.b2clogin.cn/your-tenant-name.partner.onmschina.cn/b2c_1_susi",
},
forgotPassword: {
authority: "https://your-tenant-name.b2clogin.cn/your-tenant-name.partner.onmschina.cn/b2c_1_reset",
},
editProfile: {
authority: "https://your-tenant-name.b2clogin.cn/your-tenant-name.partner.onmschina.cn/b2c_1_edit_profile"
}
},
authorityDomain: "your-tenant-name.b2clogin.cn"
}
apiConfig.js:
const apiConfig = {
b2cScopes: ["https://your-tenant-name.partner.onmschina.cn/tasks-api/tasks.read"],
webApi: "http://localhost:5000/hello"
};
步骤 4:获取 Web API 示例代码
注册 Web API 并定义其作用域后,请配置 Web API 代码,使其适用于你的 Azure AD B2C 租户。
若要获取 Web API 示例代码,请执行下列操作之一:
通过运行以下命令,从 GitHub 克隆该示例 Web API 项目:
git clone https://github.com/Azure-Samples/active-directory-b2c-javascript-nodejs-webapi.git
你还可以直接前往 GitHub 上的 Azure-Samples/active-directory-b2c-javascript-nodejs-webapi 项目。
步骤 4.1:更新 Web API
在代码编辑器中打开 config.json 文件。
使用此前创建的应用程序注册修改变量值。 然后使用作为先决条件的一部分(例如 b2c_1_susi)而创建的用户流更新
policyName
。"credentials": { "tenantName": "<your-tenant-name>", "clientID": "<your-webapi-application-ID>" }, "policies": { "policyName": "b2c_1_susi" }, "resource": { "scope": ["tasks.read"] },
步骤 4.2:启用 CORS
若要允许单页应用程序调用 Node.js Web API,需要在该 Web API 中启用跨域资源共享 (CORS)。 在生产应用程序中,密切关注哪个域正在发出请求。 在此示例中,允许来自任何域的请求。
若要启用 CORS,请使用以下中间件。 在已下载的 Node.js Web API 代码示例中,此中间件已添加到 index.js 文件中。
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Authorization, Origin, X-Requested-With, Content-Type, Accept");
next();
});
步骤 5:运行 SPA 和 Web API
现已准备好测试单页应用程序在限定作用域内对 API 的访问。 在本地计算机上同时运行 Node.js Web API 和示例 JavaScript 单页应用程序。 然后登录到该单页应用程序,并选择“调用 API”按钮向受保护的 API 发起请求。
运行 Node.js Web API
打开控制台窗口,切换到包含 Node.js Web API 示例的目录。 例如:
cd active-directory-b2c-javascript-nodejs-webapi
运行以下命令:
npm install && npm update node index.js
控制台窗口将显示托管该应用程序的端口号。
Listening on port 5000...
运行单页应用
打开另一个控制台窗口,并切换到包含 JavaScript SPA 示例的目录。 例如:
cd ms-identity-b2c-javascript-spa
运行以下命令:
npm install && npm update npm start
控制台窗口将显示该应用程序所在的端口号。
Listening on port 6420...
若要查看此应用程序,请在浏览器中转到
http://localhost:6420
。完成注册或登录过程。 成功登录后,应会看到“用户 <用户名> 已登录”的消息。
选择“调用 API” 按钮。 SPA 会将请求中的访问令牌发送到受保护的 Web API,该 Web API 将返回已登录用户的显示名称:
部署应用程序
在生产应用程序中,应用注册重定向 URI 通常是运行应用的可公开访问的终结点,比如 https://contoso.com/signin-oidc
。
可以随时在注册的应用程序中添加和修改重定向 URI。 重定向 URI 存在以下限制:
- 回复 URL 必须以方案
https
开头。 - 回复 URL 区分大小写。 其大小写必须与正在运行的应用程序的 URL 路径的大小写匹配。
后续步骤
有关本文所讨论的概念的详细信息,请参阅: