node.js - Express + Passport + Nginx - req.user 仅在实时环境中为空
问题描述
我有一个正在运行的应用程序,它使用 Passport 从我的 express API 获取用户数据以进行 Google OAuth2 识别。在实时环境request.user
中返回为空,在开发中它正在工作。
router.get('/user', (request, response) => {
response.send(request.user);
});
我已经在 Netlify 上部署了前端,在 Digital Ocean 的 nginx 服务器上部署了 express API。
我认为这与快速会议有关?我已经尝试了很多东西:
设置服务器选项如下:
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.set('trust proxy', 1);
app.use(function (request, response, next) {
response.header('Access-Control-Allow-Credentials', true);
response.header(
'Access-Control-Allow-Origin',
request.headers.origin,
);
response.header(
'Access-Control-Allow-Methods',
'GET,PUT,POST,DELETE',
);
response.header(
'Access-Control-Allow-Headers',
'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept',
);
if ('OPTIONS' == request.method) {
response.send(200);
} else {
next();
}
});
app.use(
session({
secret: process.env.COOKIE_SECRET,
resave: false,
saveUninitialized: true,
}),
);
app.use(passport.initialize());
app.use(passport.session());
我的前端 fetch 调用包含credentials
:
const response = await fetch(`${API_PATH}auth/user`, {
method: 'GET',
credentials: 'include',
});
将此添加到 nginx 配置中:
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
}
在我的本地环境中,请求标头包含一个 cookie,在实时环境中仅Set-Cookie
在响应中设置。
请在此处找到完整的存储库:https ://github.com/sanderdebr/flightscanner
解决方案
编辑
我原来的想法是正确的。我找到了您的网站并且能够确认 Chrome 正在阻止 cookie。
Set-Cookie: connect.sid=s%....g37cv9X...sbdA; Domain=flightscanner.netlify.app; Path=/; HttpOnly; Secure
This set cookie was blocked because its domain attribute was invalid with regards to the current host url.
这将解决您的问题cookie: {httpOnly: false}
,但不是正确的方法。你需要弄清楚你的 NGINX 配置有什么问题并修复它。
原来的
您的要点的第 5 行显示Path=/; HttpOnly
如果您通过 HTTPS 访问前端,那么 chrome 将忽略 Set-Cookie 标头。这暗示了您的 nginx 配置存在问题。您可以为测试做的一件事是:
app.use(
session({
secret: process.env.COOKIE_SECRET,
resave: false,
saveUninitialized: true,
cookie: {httpOnly: false},
}),
);
您需要确保并清除 Chrome 中的所有网站数据。
获取 Fiddler 4 并将其设置为代理 SSL/HTTPS 流量。这将帮助您正确获取标题并查看其他问题。此外,在 Chrome 开发工具中选择具有登录请求的条目。检查 Set-Cookie 的标题选项卡,然后检查 Cookies 选项卡。我的猜测是您会看到带有 HttpOnly 的 Set-Cookie 并且 cookie 已存储,但 chome 不会将其与您的 fetch 请求一起发送。
为了提供一些额外的帮助,我将从生产中添加部分 nginx 配置。我不能发布完整的配置,对不起。请记住,我们使用 Docker,因此某些部分将不适用,需要进行更改以匹配您的环境。
这是我们的 express/passport 会话配置:
expressSession: {
name: EXPRESS_SESSION_NAME,
secret: EXPRESS_SESSION_SECRET,
resave: false,
saveUninitialized: false,
proxy: true,
rolling: true,
cookie: {
maxAge: 15 * MINUTE,
sameSite: 'none',
secure: !isDev,
httpOnly: !isDev,
domain: EXPRESS_SESSION_COOKIE_DOMAIN,
}
},
passportSession: {
httpOnly: !isDev,
}
我们运行多个子域,其中一个是我们的 Portal 一个内部客户 React/Express 应用程序。express 部分在 docker 容器中运行。为了处理这个问题,我们需要在每个子域基础上处理 CORS。我们使用映射是因为 IF 真的很邪恶!看:https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/
# setup out mappings in HTTP block outside SERVER blocks
# Used to disabled logging
map $request_uri $loggable {
/healthcheck 0;
default 1;
}
# allow all subdomains - each subdomain will get their own server block
map $http_origin $cors_origin {
default "";
"~^https://(localhost|.*\.ourdomain.network)$" "$http_origin";
}
# Websockets
map $http_upgrade $connection_upgrade {
default upgrade;
"" close;
}
基本服务器块在这里。我们使用 NGINX 来卸载 SSL/HTTP2。我们前面有一个 AWS NLB,但无法使用 ALB,因为我们有一些使用 SSL 证书进行身份验证的用户。此配置中未显示。
# This is a server block for a subdomain
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name portal.ourdomain.network;
allow all;
access_log /dev/stdout proxy_log;
error_log stderr debug;
set $handler "https.portal.ourdomain.network";
# prevent Cross-site scripting (XSS) by enabling built in browser filter
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
add_header X-XSS-Protection "1; mode=block";
# enable HSTS (HTTP Strict Transport Security) to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
# https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security
add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload" always;
include sites/star.ourdomain.network/portal/*.nginx;
}
这是您最应该关注的部分。在这里,我们有一个用于处理 API 请求的位置,即流向我们的 express 应用程序的流量。我们让 NGINX 处理预检请求,因为它更容易且不易出错。其中一些标题超出了应有的范围。我们将在下个月左右进行测试,将它们缩减到所需的最低限度。
# API Location Block
location /api {
set $handler "$http_origin/api";
access_log /dev/stdout proxy_log;
error_log stderr debug;
resolver 127.0.0.11; # Docker DNS
proxy_redirect off;
add_header Access-Control-Allow-Origin $cors_origin;
add_header Vary Origin;
add_header Access-Control-Allow-Credentials true;
# Always allowed Accept, Accept-Language, Content-Language, Content-Type
add_header Access-Control-Allow-Headers "Origin,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range";
add_header Access-Control-Allow-Methods "OPTIONS,GET,PUT,POST,DELETE";
if ($request_method = 'OPTIONS') {
set $handler "OPTIONS:portal.ourdomain.network:443/api";
## DOMAIN/SUBDOMAIM CORS Preflight
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header Vary Origin;
add_header 'Access-Control-Allow-Methods' 'OPTIONS,GET,PUT,POST,DELETE';
# Custom headers and headers various browsers *should* be OK with but aren't
add_header 'Access-Control-Allow-Headers' 'Origin,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
# Tell client that this pre-flight info is valid for 20 days
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
add_header Sec-Fetch-Site same-site;
return 204;
}
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_connect_timeout 60;
proxy_read_timeout 60;
# We have to do it this way otherwise NGINX cannot resolve the container and will fail to start
set $proxy_host "server:4000";
proxy_pass http://$proxy_host;
}
这是服务器块设置前端版本的包含文件。我们正在考虑使此标头基于默认值。
set $s3Bucket "portal.ourdomain.network.s3-website-us-gov-west-1.amazonaws.com";
set $version "0.1.28";
这只是将前端流量代理到 AWS S3 存储桶的基本位置。
# Front End Location
location / {
set $handler "$http_origin/";
access_log /dev/stdout proxy_log;
error_log stderr debug;
ssi on;
resolver 8.8.8.8;
proxy_intercept_errors on;
proxy_redirect off;
proxy_set_header Host $s3Bucket;
proxy_hide_header x-amz-id-2;
proxy_hide_header x-amz-request-id;
add_header Access-Control-Allow-Origin $cors_origin;
add_header Vary Origin;
add_header Access-Control-Allow-Headers *;
add_header Access-Control-Allow-Methods 'GET, OPTIONS';
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_connect_timeout 60;
proxy_read_timeout 60;
rewrite ^/(.*)$ /$version/$1 break;
proxy_pass http://$s3Bucket;
}
推荐阅读
- cookies - 仅在启用第三方 cookie 时使用身份验证代码进行 Azure AD 身份验证
- c++ - 为什么作为参数传递的引用可以通过函数修改变量?
- c++ - 用字符串数组命名结构的成员
- ansible - Ansible 试用循环 - apache2、sqlite3、git install
- exception - 运行我的 ASP.NET Web 应用程序时出现异常错误,
- python - Python Django - 自定义添加用户保存方法
- node.js - node.js 的 node-postgres 模块中的池化如何工作
- ios - 无效的文档参考。文档引用必须有偶数个段,但用户有 1'
- google-apps-script - 更改时的 Google 表格时间戳
- javascript - 如何拆分数组并在列javascript中返回它