【Azure 應用服務】NodeJS Express + MSAL 實現API應用Token認證(AAD OAuth2 idToken)的認證實驗 -- passport.authenticate()

路邊兩盞燈發表於2022-06-11

問題描述

在前兩篇博文中,對NodeJS Express應用 使用MSAL + AAD實現使用者登入並獲取使用者資訊,獲取Authorization資訊 ( ID Token, Access Token).

  1. 【Azure 應用服務】NodeJS Express + MSAL 應用實現AAD整合登入並部署在App Service Linux環境中的實現步驟
  2. 【Azure 應用服務】NodeJS Express + MSAL 應用實現AAD登入並獲取AccessToken -- cca.acquireTokenByCode(tokenRequest)

而在當前這篇博文中,我們將會實現以下目的:

1)為NodeJS API應用配置Bearer Token驗證元件 passport 和 passport-azure-ad

2)實現使用idToken驗證並訪問API

【Azure 應用服務】NodeJS Express + MSAL 實現API應用Token認證(AAD OAuth2 idToken)的認證實驗 -- passport.authenticate()

 

 

實現步驟

在完成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” 準備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

下載後的檔案結構為:

【Azure 應用服務】NodeJS Express + MSAL 實現API應用Token認證(AAD OAuth2 idToken)的認證實驗 -- passport.authenticate()

第二步:修改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,所以很多引數配置有一點差別。

本次實驗中使用的引數說明如下:

  • identityMetadata (必須欄位):填寫進行OAuth 2.0 認證的 Openid-configuration地址,如在中國區的地址為  'https://login.partner.microsoftonline.cn/<your tenant id>/v2.0/.well-known/openid-configuration'
  • clientID (必須欄位): 為AAD中註冊應用的Application ID
  • audience(可選):為一個字串或者字串陣列,預設值為註冊應用的Client ID
  • validateIssuer(可選):如果不需要驗證Issuer這個引數,需要設定為false。預設值為true。當使用AAD的Openid-configuration資訊,它會通過identitymetadata中獲取 issuer資訊
  • loggingLevel(可選):AAD Validation 的日誌輸出級別,有info,error,warn可供設定
  • passReqToCallback(可選):預設值為false,使用者當請求的第一個引數中提供了驗證函式時,需要設定為true

關於BearerStrategy引數更多詳細說明請參考連結: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>

驗證效果:

【Azure 應用服務】NodeJS Express + MSAL 實現API應用Token認證(AAD OAuth2 idToken)的認證實驗 -- passport.authenticate()

 

第四步:驗證 idToken 和 accessToken 

在前端UI頁面通過登入後獲取到Token資訊, http://localhost:3000/auth

【Azure 應用服務】NodeJS Express + MSAL 實現API應用Token認證(AAD OAuth2 idToken)的認證實驗 -- passport.authenticate()

 

驗證展示動畫:

【Azure 應用服務】NodeJS Express + MSAL 實現API應用Token認證(AAD OAuth2 idToken)的認證實驗 -- passport.authenticate()

使用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)中

accessTokenAcceptedVersion 值為 2 
【Azure 應用服務】NodeJS Express + MSAL 實現API應用Token認證(AAD OAuth2 idToken)的認證實驗 -- passport.authenticate()
  1. 登入Azure 門戶,選擇Azure AD。
  2. 點選 App registrations 並選擇自己的應用,如本示例中的“ExpressWebApp”
  3. 進入應用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-Inhttps://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 apphttps://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-nodejs-webapp-msal

Example: Acquiring tokens with ADAL Node vs. MSAL Nodehttps://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

 

相關文章