如何解决我的 MERN 应用程序中的 Access-Control-Allow-Origin CORS 错误以进行 MSAL 身份验证?

Posted

技术标签:

【中文标题】如何解决我的 MERN 应用程序中的 Access-Control-Allow-Origin CORS 错误以进行 MSAL 身份验证?【英文标题】:How can I solve the Access-Control-Allow-Origin CORS error in my MERN app for MSAL auth? 【发布时间】:2021-07-15 15:38:43 【问题描述】:

我正在尝试通过单击按钮在我的 MERN 应用中通过 MSAL 进行身份验证。

但是我得到了这个错误:

访问 XMLHttpRequest 在 'https://login.microsoftonline.com/common/oauth2/v2.0/a...' (重定向自 'http:///api/auth/signin') 'http://' 已被 CORS 策略阻止:否 请求中存在“Access-Control-Allow-Origin”标头 资源。

这是我的 NodeJS 服务器的代码:

const express = require("express");
const session = require('express-session');
const authRoutes = require("./routes/auth.routes");
const msal = require('@azure/msal-node');
const cors = require("cors");
require("dotenv").config();

const app = express();
const corsOptions = 
    origin : process.env.CLIENT_URL,
    credentials: true,
    "allowedHeaders": ["sessionId", "Content-Type"],
    "exposedHeaders": ["sessionId"],
    "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
    "preflightContinue": false

app.use(cors(corsOptions));


// Demo only
app.locals.users = ;

// MSAL Config
const msalConfig = 
    auth: 
        clientId: process.env.OAUTH_APP_ID,
        authority: process.env.OAUTH_AUTHORITY,
        clientSecret: process.env.OAUTH_APP_SECRET
    ,
    system: 
        loggerOptions: 
            loggerCallback(loglevel, message, containsPii) 
                console.log(message);
            ,
            piiLoggingEnabled: false,
            logLevel: msal.LogLevel.Verbose,
        
    
;

app.locals.msalClient = new msal.ConfidentialClientApplication(msalConfig);

// Session middleware
app.use(session(
secret: 'your_secret_value_here',
resave: false,
saveUninitialized: false,
unset: 'destroy'
));

app.use("/api/auth", authRoutes);

app.get("/", (req, res) => 
    res.send("Hello World!");
);

app.listen(process.env.PORT, () => 
    console.log(`Server is running on port $process.env.PORT`);
);

module.exports = app;

这是我的 auth.controller 方法:

module.exports = 
    signIn: async (req, res) => 
        const urlParameters = 
            scopes: process.env.OAUTH_SCOPES.split(','),
            redirectUri: process.env.OAUTH_REDIRECT_URI
        ;
    
        try 
            const authUrl = await req.app.locals.msalClient.getAuthCodeUrl(urlParameters);
            res.redirect(authUrl);
         catch (error) 
            console.log(`Error: $error`);
            res.redirect("/");
        
    ,

    callback: async (req, res) => 
        const tokenRequest = 
            code: req.query.code,
            scopes: process.env.OAUTH_SCOPES.split(","),
            redirectUri: process.env.OAUTH_REDIRECT_URI
        ;
    
        try 
            const response = await req.app.locals.msalClient.acquireTokenByCode(tokenRequest);
            req.session.userId = response.account.homeAccountId;
    
            const user = await graph.getUserDetails(response.accessToken);
            req.app.locals.users[req.session.userId] = 
                displayName: user.displayName,
                email: user.mail || user.userPrincipalName,
                timeZone: user.mailboxSettings.timeZone
            ;
         catch (error) 
            console.log(`Error: $error`);
        
        
        res.redirect("/");
    ,

    signOut: async (req, res) => 
        if (req.session.userId) 
            const accounts = await req.app.locals.msalClient.getTokenCache().getAllAccounts();
            const userAccount = accounts.find(a => a.homeAccountId === req.session.userId);
    
            if (userAccount) 
                req.app.locals.msalClient.getTokenCache().removeAccount(userAccount);
            
        
    
        req.session.destroy(err => res.redirect("/"));
    
;

这里是 React 部分:

import React from 'react';
import axios from "axios";

const App = () => 
    const handleConnect = () => 
        axios(
            method: "get",
            url: `$process.env.SERVER_URL/api/auth/signin`,
            withCredentials: true
        )
        .then(res => console.log(res.data))
        .catch(err => console.log(err));
    ;

    return (
        <button onClick=handleConnect>Connect</button>
    );
;

export default App;

在我的 Azure Active Directory 管理中心,我的重定向 URI 是:

”作为“SPA”/api/auth/signin”为“Web”

【问题讨论】:

【参考方案1】:

devtools 中的 Network 选项卡有助于解决此类问题。

您可能需要处理 CORS preflight requests,通过在您的 express 应用中放置类似的内容来处理 OPTIONS 请求。

app.options('*',cors())

将此行放在app.use() 之前用于任何路线。

这在生产中有点我。哎哟!

【讨论】:

感谢您的回答,但我仍然收到相同的 CORS 错误。我需要摆脱 app.use(cors(corsOptions)) 吗? 如果您完全摆脱 CORS,您的错误应该会消失。查看浏览器开发工具的网络选项卡,了解预检请求发生了什么。 如果您使用 credentials:true 并允许所有来源 *,我不确定它是否有效。 我从我的应用程序中删除了 cors,但我仍然收到错误,但这次来自 /api/auth/signin 的请求被阻止。在 devtools 中,它还告诉我 MissingAllowOriginHeader。当我将原点设置为“”时,无论凭据的值如何,它都在告诉我原点不应该是“”。【参考方案2】:

Access-Control-Allow-Origin 设置为* 风险很大,不推荐。这意味着您允许任何来源从您的服务器接收响应。

删除 CORS 意味着将执行同源策略,因此它将不起作用。

要解决客户端和服务器之间的问题,您可以在 React 应用程序的 package.json 文件中设置一个代理,该代理将指向您的服务器:"proxy": "YourServerURI"

关于 MSAL 错误的最初问题,我建议仔细检查您的应用是否已正确注册并有权访问您的服务器。

【讨论】:

谢谢,代理解决了客户端和服务器之间的问题,现在我很苦恼,因为我认为我在 Azure AD 中设置了错误的重定向 URI... 您阅读过Node.js - MSAL Quickstart 指南吗?应该很简单。重定向 URI 必须是客户端应用程序中的路径。根据文档,它应该是http://localhost:3000/redirect

以上是关于如何解决我的 MERN 应用程序中的 Access-Control-Allow-Origin CORS 错误以进行 MSAL 身份验证?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决 NodeJS 中的这个 no-unused-vars 错误?

如何解决这个 MERN 堆栈 filebase64 错误?

MERN 应用程序中的 Babel 配置

获取“被 CORS 策略阻止:请求的资源上不存在 'Access-Control-Allow-Origin' 标头。”使用 Axios 使用 MERN 堆栈

如何在 MERN 应用中实现登录功能?

如何使用 MERN 堆栈构建动态单页 Web 应用程序?