問題描述
在上一篇博文 “【Azure 應用服務】NodeJS Express + MSAL 應用實作AAD內建登入并部署在App Service Linux環境中的實作步驟”中,實作了登入,并擷取登入使用者在AAD中的個人資訊,但是沒有一個顯示的方法輸出所擷取到的Access Token,則通過建立Express項目,加載MSAL的代碼實作此目的。
實作步驟
第一步:建立 NodeJS Express項目,并添加@azure/msal-node 項目包
前提條件:安裝 Node.js 和 VS Code
使用npm安全express項目生成器
npm install -g express-generator
在目前目錄在生成 express項目預設檔案
express --view=hbs
開始生成項目檔案
npm install
安裝MSAL package
npm install --save @azure/msal-node
項目生成後的完整路徑
myExpressWebApp/
├── bin/
| └── wwww
├── public/
| ├── images/
| ├── javascript/
| └── stylesheets/
| └── style.css
├── routes/
| ├── index.js
| └── users.js
├── views/
| ├── error.hbs
| ├── index.hbs
| └── layout.hbs
├── app.js
└── package.json
第二步:在 app.js 中添加 MSAL object,添加 '/auth' 接口登入AAD并擷取Access Token
引入 msal 對象
const msal = require('@azure/msal-node');
配置AAD Authentication 參數 clientId, authority 和 clientSecret (與上一篇博文中第一步相同, 也需要添加 http://localhost:3000/redirect 在 AAD注冊應用的Redirect URIs中)。
// Authentication parameters
const config = {
auth: {
clientId: " Enter_the_Application_Id_Here",
authority: "https://login.partner.microsoftonline.cn/<#Enter_the_Tenant_Info_Here>",
clientSecret: "xxxxxx.xxxxxxxxxxxxxxxxx" #Enter_the_Client_Secret_Here
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: msal.LogLevel.Verbose,
}
}
};
const REDIRECT_URI = "http://localhost:3000/redirect";
然後根據上一步的config參數初始化 msal confidential client applicaiton對象
// Initialize MSAL Node object using authentication parameters
const cca = new msal.ConfidentialClientApplication(config);
最後,實作 /auth 和 /redirect 接口代碼 (/auth 是登入AAD的入口,登入成功後由AAD回調/redirect接口,輸出Access Token内容
app.get('/auth', (req, res) => {
// Construct a request object for auth code
const authCodeUrlParameters = {
scopes: ["user.read"],
redirectUri: REDIRECT_URI,
};
// Request auth code, then redirect
cca.getAuthCodeUrl(authCodeUrlParameters)
.then((response) => {
res.redirect(response);
}).catch((error) => res.send(error));
});
app.get('/redirect', (req, res) => {
// Use the auth code in redirect request to construct
// a token request object
const tokenRequest = {
code: req.query.code,
scopes: ["user.read"],
redirectUri: REDIRECT_URI,
};
// Exchange the auth code for tokens
cca.acquireTokenByCode(tokenRequest)
.then((response) => {
res.send(response);
}).catch((error) => res.status(500).send(error));
});
完整 app.js 代碼為:
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
const msal = require('@azure/msal-node');
// Authentication parameters
const config = {
auth: {
clientId: " Enter_the_Application_Id_Here",
authority: "https://login.partner.microsoftonline.cn/<#Enter_the_Tenant_Info_Here>",
clientSecret: "xxxxxx.xxxxxxxxxxxxxxxxx" #Enter_the_Client_Secret_Here
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: msal.LogLevel.Verbose,
}
}
};
const REDIRECT_URI = "http://localhost:3000/redirect";
// Initialize MSAL Node object using authentication parameters
const cca = new msal.ConfidentialClientApplication(config);
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.get('/auth', (req, res) => {
// Construct a request object for auth code
const authCodeUrlParameters = {
scopes: ["user.read"],
redirectUri: REDIRECT_URI,
};
// Request auth code, then redirect
cca.getAuthCodeUrl(authCodeUrlParameters)
.then((response) => {
res.redirect(response);
}).catch((error) => res.send(error));
});
app.get('/redirect', (req, res) => {
// Use the auth code in redirect request to construct
// a token request object
const tokenRequest = {
code: req.query.code,
scopes: ["user.read"],
redirectUri: REDIRECT_URI,
};
// Exchange the auth code for tokens
cca.acquireTokenByCode(tokenRequest)
.then((response) => {
res.send(response);
}).catch((error) => res.status(500).send(error));
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
運作效果動畫展示:

參考資料
NodeJS Express + MSAL 應用實作AAD內建登入并部署在App Service Linux環境中的實作步驟:https://www.cnblogs.com/lulight/p/16353145.html
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
當在複雜的環境中面臨問題,格物之道需:濁而靜之徐清,安以動之徐生。 雲中,恰是如此!