我在 Azure 上部署了一个使用 Microsoft 身份验证的简单登录和注销节点应用程序。我希望使用 Entra ID 登录后,重定向页面可以返回带有用户信息的主页。但登录后,页面仍然没有显示用户信息。该应用程序在本地运行良好,但在 Azure 中运行不佳。我想知道为什么。
登录前首页:欢迎您,请登录。
登录后正确首页:用户名、邮箱。
登录后首页错误:欢迎您,请登录。
一些代码:
const express = require("express");
const ejs = require("ejs");
const msal = require("@azure/msal-node");
const jwt = require("jsonwebtoken");
const session = require("express-session");
require("dotenv").config();
const app = express();
app.set("view engine", "ejs");
app.use(
session({
secret: process.env.SESSION_SECRET || "local_default_secret",
resave: false,
saveUninitialized: true,
cookie: { secure: true },
})
);
const msalConfig = {
auth: {
clientId: process.env.AZURE_CLIENT_ID,
authority: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}`,
clientSecret: process.env.AZURE_CLIENT_SECRET,
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: msal.LogLevel.Verbose,
},
},
};
const pca = new msal.ConfidentialClientApplication(msalConfig);
function getRedirectUri() {
return process.env.PROD_REDIRECT_URI;
}
app.get("/", (req, res) => {
res.render("index", {
isAuthenticated: req.session.isAuthenticated,
user: req.session.user || null,
});
});
app.get("/login", async (req, res) => {
try {
const authCodeUrlParameters = {
scopes: ["user.read"],
redirectUri: getRedirectUri(),
};
const loginUrl = await pca.getAuthCodeUrl(authCodeUrlParameters);
res.redirect(loginUrl);
} catch (error) {
console.error(error);
res.status(500).send("Error building auth code URL");
}
});
app.get("/redirect", async (req, res) => {
const tokenRequest = {
code: req.query.code,
scopes: ["user.read"],
redirectUri: getRedirectUri(),
};
try {
const response = await pca.acquireTokenByCode(tokenRequest);
req.session.isAuthenticated = true;
req.session.user = jwt.decode(response.accessToken);
console.log("I am here!");
res.redirect("/");
console.log("I am still here!");
console.log("Session here: ", req.session);
} catch (error) {
console.error(error);
if (error.name === "ClientAuthError") {
res.status(401).send("Authentication failed. Please try to login again.");
} else {
res.status(500).send("Error acquiring token");
}
}
});
app.get("/signout", (req, res) => {
req.session.destroy((err) => {
if (err) {
console.error(err);
return res.status(500).send("Error during sign out.");
}
// Determine the post logout redirect URI based on the environment
const postLogoutRedirectUri = process.env.PROD_URI;
// Redirect to Azure AD logout URL
const tenantId = process.env.AZURE_TENANT_ID;
const logoutUri = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/logout?post_logout_redirect_uri=${postLogoutRedirectUri}`;
res.redirect(logoutUri);
});
});
const port = process.env.PORT;
app.listen(port, () => console.log(`Server running on port ${port}`));
从上面的屏幕截图中可以看到,用户可以通过身份验证,应用程序可以获取令牌和所有用户信息,并且页面应该重定向到包含用户信息的主页。但它永远不会以这种方式发生。
我希望弄清楚为什么登录后页面无法正确显示正确的信息。可能是什么原因?
为了处理 Azure AD 身份验证并在成功登录后显示用户信息,我使用
passport
和 passport-azure-ad
库来处理身份验证过程。它手动管理会话和用户身份验证状态。
Passport-azure-ad 包让您可以在 Express 应用程序中轻松启动和运行 Azure AD 身份验证。
代码:
//app.js
const express = require('express');
const passport = require('passport');
const OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
const app = express();
// Configure session
app.use(require('express-session')({ secret: 'your-secret-key', resave: true, saveUninitialized: true }));
// Initialize passport
app.use(passport.initialize());
app.use(passport.session());
// Ensure authenticated middleware
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/');
}
// Configure Azure AD authentication
passport.use(new OIDCStrategy({
identityMetadata: 'https://login.microsoftonline.com/tenantId/v2.0/.well-known/openid-configuration',
clientID: 'clientID',
responseType: 'code',
responseMode: 'query',
redirectUrl: 'http://localhost:3000/auth/callback',
allowHttpForRedirectUrl: true,
clientSecret: 'clientSecret',
validateIssuer: false,
passReqToCallback: false,
scope: ['openid', 'profile'],
},
(iss, sub, profile, accessToken, refreshToken, done) => {
return done(null, profile);
}));
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((obj, done) => done(null, obj));
// Routes
app.get('/', (req, res) => {
if (req.isAuthenticated()) {
res.send(`Welcome, ${req.user.displayName}! <a href="/logout">Logout</a>`);
} else {
res.send('Home Page <a href="/login">Login</a>');
}
});
app.get('/login', passport.authenticate('azuread-openidconnect'));
app.get('/auth/callback',
passport.authenticate('azuread-openidconnect', { failureRedirect: '/' }),
(req, res) => {
res.redirect('/home');
});
app.get('/home', ensureAuthenticated, (req, res) => {
res.send(`Welcome, ${req.user.displayName}! <a href="/logout">Logout</a>`);
});
app.get('/logout', (req, res) => {
req.logout();
res.redirect('/');
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
输出
本地
部署到应用服务
请参考下面的MSDoc