【Azure 应用服务】NodeJS Express + MSAL 实现API应用Token认证(AAD OAuth2 idToken)的认证实验 -- passport.authenticate('oauth-bearer', {session: false})
问题描述
在前两篇博文中,对NodeJS Express应用 使用MSAL + AAD实现用户登录并获取用户信息,获取Authorization信息 ( ID Token, Access Token).
【Azure 应用服务】NodeJS Express + MSAL 应用实现AAD集成登录并部署在App Service Linux环境中的实现步骤
【Azure 应用服务】NodeJS Express + MSAL 应用实现AAD登录并获取AccessToken -- cca.acquireTokenByCode(tokenRequest)
而在当前这篇博文中,我们将会实现以下目的:
1)为NodeJS API应用配置Bearer Token验证组件 passport 和 passport-azure-ad
2)实现使用idToken验证并访问API

实现步骤
在完成Azure AD中的注册应用配置并且根据博文“ NodeJS Express + MSAL 应用实现AAD登录并获取AccessToken -- cca.acquireTokenByCode(tokenRequest): https://www.cnblogs.com/lulight/p/16357246.html”完成用户登录的前端应用后,参考官方示例“Enable authentication in your own Node.js web API by using Azure Active Directory B2C : https://docs.microsoft.com/en-us/azure/active-directory-b2c/enable-authentication-in-node-web-app-with-api”.
第一步:下载示例代码
git clone https://github.com/Azure-Samples/active-directory-b2c-javascript-nodejs-webapi.git
Install app dependencies
cd active-directory-b2c-javascript-nodejs-webapi
npm install
npm update
下载后的文件结构为:

第二步:修改config.json 文件和index.js中的 identityMetadata 值
options中即为 BearerStrategy的配置参数,因为当前不适用AAD B2C,而是直接使用AAD,所以isB2C就需要设置为false,
const options = {
identityMetadata: 'https://login.partner.microsoftonline.cn/xxxxxxxx-66d7-xxxx-8f9f-xxxxxxxxxxxx/v2.0/.well-known/openid-configuration',
clientID: ##clientID,
audience: ##clientID,
validateIssuer: true,
loggingLevel: 'info',
passReqToCallback: false
}
因为参考文档中使用的试AAD B2C来认证Token,而本示例中使用的是AAD来认证Token,所以很多参数配置有一点差别。 BearerStrategy的详细参数说明如下:
identityMetadata(Required)The metadata endpoint provided by the Microsoft Identity Portal that provides the keys and other important information at runtime. Examples:
- v1 tenant-specific endpoint
https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com/.well-known/openid-configuration
https://login.microsoftonline.com/your_tenant_guid/.well-known/openid-configuration
- v1 common endpoint
https://login.microsoftonline.com/common/.well-known/openid-configuration
- v2 tenant-specific endpoint
https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com/v2.0/.well-known/openid-configuration
https://login.microsoftonline.com/your_tenant_guid/v2.0/.well-known/openid-configuration
- v2 common endpoint
https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
For B2C, you can only use v2 tenant-specific endpoint.
clientID(Required)The client ID of your application in AAD (Azure Active Directory)
passReqToCallback(Conditional)Required to set to true if using
reqas the first paramter in the verify function, default value is false. See section 4.2.1.3 for more details.isB2C(Conditional)Required to set to true if you are using B2C tenant.
policyName(Conditional)Required if you are using B2C tenant. It is a string starting with 'B2C_1_' (case insensitive).
validateIssuer(Conditional)Required to set to false if you don't want to validate issuer, default value is true. We validate the
issclaim in id_token against user providedissuervalues and the issuer value we get from tenant-specific endpoint. If you use common endpoint foridentityMetadataand you want to validate issuer, then you must provideissuer, or providetenantIdOrNamein passport.authenticate.issuer(Conditional)This can be a string or an array of strings. See
validateIssuerfor the situation that requiresissuer.allowMultiAudiencesInToken(Conditional)Required if you allow access_token whose
audclaim contains multiple values.scope(Optional)This value is an array of scopes you accept. If this value is provided, we will check if the token contains one of these accepted scopes. If this value is not provided, we won't check token scopes.
audience(Optional)Must be a string or an array of strings. We invalidate the
audclaim in access_token againstaudience. The default value foraudienceisclientID.loggingLevel(Optional)Logging level. 'info', 'warn' or 'error'.
loggingNoPII(Optional)If this is set to true, no personal information such as tokens and claims will be logged. The default value is true.
clockSkew(Optional)This value is the clock skew (in seconds) allowed in token validation. It must be a positive integer. The default value is 300 seconds.
proxy(optional)
This value is the proxy settings object: { port: 'proxyport', host: 'proxyhost', protocol: 'http' }
文档地址:https://github.com/AzureAD/passport-azure-ad#42-bearerstrategy
第三步:访问API接口(/hello 需要Authorization, /public 不需要Authorization)
在index.js代码中,实现了两个接口 /hello 和 /public。 /hello 接口添加了passport.authenticate认证,访问需要携带Authorization (JWT Token),而/public则无需认证。
//<ms_docref_protected_api_endpoint>
// API endpoint, one must present a bearer accessToken to access this endpoint
app.get('/hello',
passport.authenticate('oauth-bearer', {session: false}),
(req, res) => {
console.log(req.headers.authorization);
console.log('Validated claims: ', req.authInfo); // Service relies on the name claim.
res.status(200).json({'name': req.authInfo['name']});
}
);
//</ms_docref_protected_api_endpoint> //<ms_docref_anonymous_api_endpoint>
// API anonymous endpoint, returns a date to the caller.
app.get('/public', (req, res) => res.send( {'date': new Date() } ));
//</ms_docref_anonymous_api_endpoint>
验证效果:

第四步:验证 idToken 和 accessToken
在前端UI页面通过登录后获取到Token信息, http://localhost:3000/auth

验证展示动画:

使用accessTokne的错误日志
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"In Strategy.prototype.authenticate: received metadata","time":"2022-06-11T06:15:43.024Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"In Strategy.prototype.authenticate: we will validate the options","time":"2022-06-11T06:15:43.025Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"In Strategy.prototype.authenticate: access_token is received from request header","time":"2022-06-11T06:15:43.025Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"In Strategy.prototype.jwtVerify: token is decoded","time":"2022-06-11T06:15:43.027Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"working on key","time":"2022-06-11T06:15:43.028Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"PEMkey generated","time":"2022-06-11T06:15:43.033Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"authentication failed due to: In Strategy.prototype.jwtVerify: cannot verify token","time":"2022-06-11T06:15:43.036Z","v":0}
GET /hello 401 1.556 ms - -
使用idToken的正确日志
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"In Strategy.prototype.authenticate: received metadata","time":"2022-06-11T06:16:25.102Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"In Strategy.prototype.authenticate: we will validate the options","time":"2022-06-11T06:16:25.102Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"In Strategy.prototype.authenticate: access_token is received from request header","time":"2022-06-11T06:16:25.103Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"In Strategy.prototype.jwtVerify: token is decoded","time":"2022-06-11T06:16:25.104Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"working on key","time":"2022-06-11T06:16:25.104Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"PEMkey generated","time":"2022-06-11T06:16:25.105Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"In Strategy.prototype.jwtVerify: token is verified","time":"2022-06-11T06:16:25.107Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"MININT-S4MGVOU","pid":17316,"level":30,"msg":"In Strategy.prototype.jwtVerify: We did not pass Req back to Callback","time":"2022-06-11T06:16:25.107Z","v":0}
Validated claims: {
aud: 'xxxxx-c6fd-xxx-9dac-xxxxxx',
iss: 'https://login.partner.microsoftonline.cn/xxxxx-c6fd-xxx-9dac-xxxxxx/v2.0',
iat: 1654924192,
nbf: 1654924192,
exp: 1654928092,
name: 'your name here',
oid: 'xxxxx-c6fd-xxx-9dac-xxxxxx',
preferred_username: 'xxxx@xxxx.partner.onmschina.cn',
rh: '0.xxxxxxxxx-xxxxxxxxxxxxxx.',
sub: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx',
tid: 'x-66d7-47a8-xx-xxx',
uti: 'xxxxxxxxxxxxxxxxxxxxxxxxx',
ver: '2.0'
}
GET /hello 200 11.557 ms - 16
[可选]第五步:修改AAD注册应用的accessTokenAcceptedVersion
因为中国区AAD目前生成的Token为OAuth v1.0, 而在API应用中 identityMetadata 使用的是v2.0的openid-configration。所以需要在ADD中修改当前注册应用的清单文件(Mainfest)中

- 登录Azure 门户,选择Azure AD。
- 点击 App registrations 并选择自己的应用,如本示例中的“ExpressWebApp”
- 进入应用Overview页面后,选择左侧导航中“Manifest”清单页面。修改 accessTokenAcceptedVersion 的值为2,保存即可。
参考资料
Configure authentication in a sample Node.js web API by using Azure Active Directory B2C: https://docs.microsoft.com/en-us/azure/active-directory-b2c/configure-authentication-in-sample-node-web-app-with-api#step-4-get-the-web-api-sample-code
Microsoft Azure Active Directory Passport.js Plug-In:https://github.com/AzureAD/passport-azure-ad#42-bearerstrategy
Tutorial: Sign in users and acquire a token for Microsoft Graph in a Node.js & Express web app: https://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-nodejs-webapp-msal
Example: Acquiring tokens with ADAL Node vs. MSAL Node:https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-node-migration#example-acquiring-tokens-with-adal-node-vs-msal-node
NodeJS Express + MSAL 应用实现AAD集成登录并部署在App Service Linux环境中的实现步骤:https://www.cnblogs.com/lulight/p/16353145.html
NodeJS Express + MSAL 应用实现AAD登录并获取AccessToken -- cca.acquireTokenByCode(tokenRequest):https://www.cnblogs.com/lulight/p/16357246.html
【Azure 应用服务】NodeJS Express + MSAL 实现API应用Token认证(AAD OAuth2 idToken)的认证实验 -- passport.authenticate('oauth-bearer', {session: false})的更多相关文章
- 【Azure 应用服务】NodeJS Express + MSAL 应用实现AAD集成登录并部署在App Service Linux环境中的实现步骤
问题描述 实现部署NodeJS Express应用在App Service Linux环境中,并且使用Microsoft Authentication Library(MSAL)来实现登录Azure ...
- 【Azure 应用服务】NodeJS Express + MSAL 应用实现AAD登录并获取AccessToken -- cca.acquireTokenByCode(tokenRequest)
问题描述 在上一篇博文 "[Azure 应用服务]NodeJS Express + MSAL 应用实现AAD集成登录并部署在App Service Linux环境中的实现步骤"中, ...
- nodejs+express+mongodb写api接口的简单尝试
1:启动mongodb服务 我的mongoDB的安装目录:E:\mongoDB\bin,版本:3.4.9 打开cmd -> e:(进入e盘) -> cd mongoDB/bin(进入mo ...
- Azure 应用服务中的 API 应用、ASP.NET 和 Swagger 入门
学习内容: 如何通过 Visual Studio 2015 中的内置工具在 Azure 应用服务中创建和部署 API 应用. 如何使用 Swashbuckle NuGet 包动态生成 Swagger ...
- NodeJs接口token认证express框架passport实现方式Bearer认证
1.生成一个简单的express项目(命令:express passport-test),项目结构如下: 2.添加项目依赖: npm install passport --save npm insta ...
- Nodejs Express 4.X 中文API 1--- Application篇
相关阅读: Express 4.X API 翻译[一] -- Application篇 Express4.XApi 翻译[二] -- Request篇 Express4.XApi 翻译[三] -- ...
- Nodejs Express 4.X 中文API 4--- Router篇
相关阅读: Express 4.X API 翻译[一] -- Application篇 Express4.XApi 翻译[二] -- Request篇 Express4.XApi 翻译[三] -- ...
- Nodejs Express 4.X 中文API 3--- Response篇
相关阅读: Express 4.X API 翻译[一] -- Application篇 Express4.XApi 翻译[二] -- Request篇 Express4.XApi 翻译[三] -- ...
- Nodejs Express 4.X 中文API 2--- Request篇
相关阅读: Express 4.X API 翻译[一] -- Application篇 Express4.XApi 翻译[二] -- Request篇 Express4.XApi 翻译[三] -- ...
随机推荐
- 结合手工注入编写一个SQL盲注脚本——以SQLi-Labs less16为例
一.分析测试注入点 1.抓包,查看响应数据包 2.先随便输入一个账号密码,再测试万能密码 1") or 1=1 -- # 3.发现响应数据包的Content-Length字段值不同.错误状态 ...
- android软件简约记账app开发day08-时间对话框的书写+改bug,改bug
android软件简约记账app开发day08-时间对话框的书写+改bug,改bug 绘制对话跨页面 在添加记账信息功能中,我提供了用户添加备注添加事件的功能,设计是点击时间会弹出一个时间对话框供用户 ...
- WEB安全信息收集
目录 信息收集 子域名&敏感信息 敏感信息收集--Googlehack 敏感信息收集--收集方向 空间测绘引擎域名资产收集 子域名收集 WEB指纹 端口扫描 IP查询 cms识别 WAF识别 ...
- Redis 内存满了怎么办?这样设置才正确!
上回在<Redis 数据过期了会被立马删除么?>说到如果过期的数据太多,定时删除无法删除完全(每次删除完过期的 key 还是超过 25%),同时这些 key 再也不会被客户端请求,就无法走 ...
- springmvc04-数据处理
数据处理 我们把它分为三种情况来分析,这样我们对于数据处理会有更好的理解 1.提交的域名称和处理方法的参数名一致 提交数据 : http://localhost:8080/hello?name=xi ...
- python数据处理matplotlib入门(2)-利用随机函数生成变化图形
综合前述的类.函数.matplotlib等,完成一个随机移动的过程(注意要确定移动的次数,比如10万次),每次行走都完全是随机的,没有明确的方向,结果是由一系列随机决策确定的,最后显示出每次移动的位置 ...
- 攻防世界-MISC:János-the-Ripper
这是攻防世界MISC高手进阶区的题目: 点击下载附件一,解压后得到一个没有后缀的文件,老规矩用010editor打开,发现存在一个flag.txt文件 用foremost分离一下: flag.txt被 ...
- 浅谈stm32的外部中断
简述 本文简单介绍stm32外部中断的一般操作步骤,后续会补充外部中断的相关内容 stm32的中断控制器支持19个外部中断/事件请求: line0~line15:这16条line分别对应不同GPIO口 ...
- CentOS 7上搭建Zabbix4.0
开源Linux 一个执着于技术的公众号 zabbix介绍 Zabbix 是一个基于 WEB 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案.它能监视各种网络参数,保证服务器系统的安全运 ...
- Linux-I/O模型详解
I/O介绍 I/O通常有内存IO.网络I/O.磁盘I/O等,但我们通常说的是网络I/O以及磁盘I/O.网络I/O:本质是socket读取 每次I/O请求,都会有两个阶段组成: 第一步:等待数据,即数据 ...