【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”.

 

第一步:下載示例程式碼

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,所以很多引數配置有一點差別。 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 req as 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 iss claim in id_token against user provided issuer values and the issuer value we get from tenant-specific endpoint. If you use common endpoint for identityMetadata and you want to validate issuer, then you must provide issuer, or provide tenantIdOrName in passport.authenticate.

  • issuer (Conditional)

    This can be a string or an array of strings. See validateIssuer for the situation that requires issuer.

  • allowMultiAudiencesInToken (Conditional)

    Required if you allow access_token whose aud claim 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 aud claim in access_token against audience. The default value for audience is clientID.

  • 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>

驗證效果:

【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

 

相關文章