跨域会话Cookie(Heroku上的Express API + Netlify上的React应用程序)

8ftvxx2r  于 2022-11-13  发布在  React
关注(0)|答案(2)|浏览(153)

我有一个React应用程序在node.js/Express中调用API。
前端部署在Netlify(https)上,后端部署在Heroku(https)上。
我的问题:

  • 开发环境中的一切工作正常(localhost)
  • 在生产环境(Netlify/Heroku)中,API调用注册和登录似乎可以正常工作,但会话cookie没有存储在浏览器中。因此,API中任何其他对受保护路由的调用都将失败(因为我没有收到用户凭据)。

  • 说话是廉价的,告诉我密码 *

后端(快速API):

require('./configs/passport');

// ...

const app = express();

// trust proxy (https://stackoverflow.com/questions/64958647/express-not-sending-cross-domain-cookies)
app.set("trust proxy", 1); 

app.use(
  session({
    secret: process.env.SESSION_SECRET,
    cookie: {
      sameSite: process.env.NODE_ENV === "production" ? 'none' : 'lax',
      maxAge: 60000000,
      secure: process.env.NODE_ENV === "production",
    },
    resave: true,
    saveUninitialized: false,
    ttl: 60 * 60 * 24 * 30
  })
);

app.use(passport.initialize());
app.use(passport.session());

// ...

app.use(
  cors({
    credentials: true,
    origin: [process.env.FRONTEND_APP_URL]
  })
);

//...

app.use('/api', require('./routes/auth-routes'));
app.use('/api', require('./routes/item-routes'));

CRUD端点(例如,项目路径. js):

// Create new item
router.post("/items", (req, res, next) => {
    Item.create({
        title: req.body.title,
        description: req.body.description,
        owner: req.user._id // <-- AT THIS POINT, req.user is UNDEFINED
    })
    .then(
        // ...
    );
});

前端(React应用程序):

  • 使用Axios并将“withCredentials”选项设置为true...
    用户注册和登录:
class AuthService {
  constructor() {
    let service = axios.create({
      baseURL: process.env.REACT_APP_API_URL,
      withCredentials: true
    });
    this.service = service;
  }
  
  signup = (username, password) => {
    return this.service.post('/signup', {username, password})
    .then(response => response.data)
  }

  login = (username, password) => {
    return this.service.post('/login', {username, password})
    .then(response => response.data)
  }
   
  //...
}

正在创建新项目...:

axios.post(`${process.env.REACT_APP_API_URL}/items`, {
        title: this.state.title,
        description: this.state.description,
    }, {withCredentials:true})
    .then( (res) => {
        // ...
    });
lh80um4z

lh80um4z1#

简短答案:

它没有像预期的那样工作,因为我是在Chrome Incognito上测试的,默认情况下,Chrome在Incognito模式下阻止第三方Cookiemore details)。
下面是一个列表,列出了如果您遇到类似问题时需要检查的一些事项;)

检查清单

如果有帮助的话,这里有一个清单,上面有你主要需要的不同东西;)

*(后端)添加“信任代理”选项

如果您要在Heroku上部署,请添加以下行(您可以在会话设置之前添加它)。

app.set("trust proxy", 1);

*(后端)检查您的会话设置

特别要检查选项sameSitesecuremore details here)。
下面的代码将在生产环境中设置sameSite: 'none'secure: true

app.use(
  session({
    secret: process.env.SESSION_SECRET || 'Super Secret (change it)',
    resave: true,
    saveUninitialized: false,
    cookie: {
      sameSite: process.env.NODE_ENV === "production" ? 'none' : 'lax', // must be 'none' to enable cross-site delivery
      secure: process.env.NODE_ENV === "production", // must be true if sameSite='none'
    }
  })
);

*(后端)CORS配置

app.use(
  cors({
    credentials: true,
    origin: [process.env.FRONTEND_APP_URL]
  })
);

*(后端)环境变量

在Heroku中设置环境变量。例如:

FRONTEND_APP_URL = https://my-project.netlify.app

重要提示:对于CORS URL,请避免在结尾处使用尾随斜杠。以下语句可能不起作用:

FRONTEND_APP_URL = https://my-project.netlify.app/ --> avoid this trailing slash!

*(前端)发送凭据

确保在API调用中发送凭据(您需要为所有对API的调用都发送凭据,包括用户登录调用)。
如果你使用的是axios,你可以使用withCredentials选项。例如:

axios.post(`${process.env.REACT_APP_BACKEND_API_URL}/items`, {
        title: this.state.title,
        description: this.state.description,
    }, {withCredentials:true})
    .then( (res) => {
        // ...
    });

*(浏览器)检查第三方Cookie的配置

为了进行测试,您可能需要确保使用的是每个浏览器提供的默认配置。
例如,截至2021年,Chrome在“隐身”模式下(而不是“正常”模式下)阻止第三方Cookie,所以你可能希望有这样的东西:

*...并处理浏览器限制...:

最后,请记住,每个浏览器对第三方Cookie都有不同的政策,一般来说,这些限制预计在未来几年会增加。
例如,Chrome预计将在2023年的某个时候阻止第三方Cookie(source)。
如果您的应用程序需要绕过这些限制,以下是一些选项:

  • 在同一域下实施后端和前端
  • 在同一域的子域下实施后端和前端(例如,example.com和api.example.com)
  • 将后端API置于代理之下(如果您使用Netlify,则可以轻松地setup a proxy using a _redirects file
ncecgwcz

ncecgwcz2#

问题归结到第三方Cookie。
如果您要从 * server.herokuapp.com * 向 * site.herokuapp.com * 发送数据,则会遇到此问题。

解决方案是为Heroku应用程序使用自定义域。

请查看此帖子以了解更多详细信息:Cookies Only set in Chrome - not set in Safari, Mobile Chrome, or Mobile Safari

相关问题