无法检索 Express-Session 和 Connect-Session-Firebase 的 Session Cookie
Unable to Retrieve Session Cookie of Express-Session and Connect-Session-Firebase
我正在尝试完成 oauth-1 握手。我的后端托管在 Google Functions 上。我将快速浏览流程以获得上下文:
- 用户在前端点击以将 Etsy 连接添加到我的应用程序。然后,我的前端联系后端中的适当端点以启动此过程。
- 我的后端从 Etsy 请求一个令牌,我收到一个令牌、一个秘密和一个 url 将用户重定向到,以便他们可以登录。我尝试将令牌和秘密存储在一个 session cookie 供以后使用——因为我使用的是 Google 函数,后端本身不会“持久化”,所以我试图存储这些值以便在下一个 execution/stage. (注意:在请求令牌时,我提供了一个回调 URL,它将在用户登录时联系。)
- 我的前端从第 2 步收到 url 并将用户重定向到那里登录。
- 用户通过该登录批准,并调用回调 URL。这包括验证器。然后我必须使用步骤 2 中的验证器和令牌和秘密获取 accessToken。虽然我认为这些将在 session 中检索,但它们都是未定义的,因此我对 accessToken 的请求失败了。
在我看来,每次调用都会创建一个新的 session,无论是否已经存在 session。如果我在第 1 步和第 2 步检查 session,它们的时间戳是不同的,这让我认为第 2 步不是从第 1 步检索 session,而是创建一个新的——很可能是因为我的表现 app.use(session)
.
我使用的技术列表:
- node.js
- 快递
- express-session
- firebase-functions
- connect-session-firebase(也尝试过 firestore-store,对下面的代码进行非常轻微的调整以匹配其基本设置。)
这是我的后端实现:
const url = require('url');
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const express = require("express");
const app = express();
const cors = require("cors")
const session = require('express-session')
const FirebaseStore = require('connect-session-firebase')(session);
// Etsy OAuth
// For Etsy OAuth
const etsyjs = require('etsy-js');
const client = etsyjs.client({
key: process.env.ETSY_KEY,
secret: process.env.ESTY_SECRET,
callbackURL: '<location>/authorize'
});
const ref = admin.initializeApp({
credential: admin.credential.cert(json), //json retrieved and put here
databaseURL: 'https://<app>.firebaseio.com'
});
app.use(cors())
app.use(session({
store: new FirebaseStore({
database: ref.database(),
}),
secret: 'My secret',
resave: true,
saveUninitialized: true,
name: '__session',
cookie: {
maxAge : 60000,
secure: false,
httpOnly: false
}
}
));
// Step 1 for backend of OAuth
app.get('/begin-oauth', (req, res) => {
res.setHeader('Cache-Control', 'private'); // I read somewhere this needs to be set, but I am not seeing a difference either way with it.
return client.requestToken((err, response) => {
if (err) {
return console.log(err);
}
req.session.token = response.token;
req.session.sec = response.tokenSecret;
res.status('200').send(response) // returning url to send the user there via frontend.
});
});
// Step 2 for backend of OAuth
app.get('/authorize', (req, res) => {
let query, verifier;
query = url.parse(req.url, true).query;
verifier = query.oauth_verifier;
// session at this point should have token and sec stored from before but are not present.
return client.accessToken(req.session.token, req.session.sec, verifier, (err, response) => {
if (err) {
console.log(err);
}
req.session.token = response.token;
req.session.sec = response.tokenSecret;
// Finish up.
});
});
好的,现在一切正常。有几件事让它发挥作用。
- 在后端调用中包含凭据,现在看起来像这样:
return await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
}).then(...)
- 正在将凭据添加到 cors 设置 server-side。所以现在看起来像这样:
var corsOptions = {
credentials: true,
origin: true
}
app.use(cors(corsOptions))
- 这些仍然没有完全解决问题。最终解决问题的是我从浏览器中清除了缓存。我相信这是真正解决问题的方法!尝试这个简单的事情花了多长时间绝对是痛苦的。
更新:
所以上面修复了开发环境中的所有内容,但当我实际部署到 Google 函数时却没有。那时我遇到了一系列围绕 cors 的新问题。这是我收到的错误消息:
这是我为解决此问题所做的更改:
app.use(function(req, res, next) {
var allowedOrigins = ['http://localhost:3000', 'http://localhost:5000', 'https://www.<site>.com', 'https://<app-location>.cloudfunctions.net', 'https://<app>.firebaseio.com']
var origin = req.headers.origin;
functions.logger.log('Checking headers:')
functions.logger.log(origin)
functions.logger.log(req.headers)
if(allowedOrigins.indexOf(origin) > -1){
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Credentials', true);
return next();
});
并且我将 cookie 更新为:
cookie: {
maxAge: 30*24*60*60*1000,
secure: true,
httpOnly: false,
sameSite: 'none'
}
^ 特别是,我设置了 secure: true
和 sameSite: 'none'
更新 2:
有了上面的内容,我让它以部署的形式工作,但它不能在 Chrome 隐身 window 中工作,这让我很紧张,也许它能工作只是因为我仍然缓存了一些东西我没有追踪。原来这是 incognito windows 的安全功能。您必须将其关闭才能使会话正常工作:
我正在尝试完成 oauth-1 握手。我的后端托管在 Google Functions 上。我将快速浏览流程以获得上下文:
- 用户在前端点击以将 Etsy 连接添加到我的应用程序。然后,我的前端联系后端中的适当端点以启动此过程。
- 我的后端从 Etsy 请求一个令牌,我收到一个令牌、一个秘密和一个 url 将用户重定向到,以便他们可以登录。我尝试将令牌和秘密存储在一个 session cookie 供以后使用——因为我使用的是 Google 函数,后端本身不会“持久化”,所以我试图存储这些值以便在下一个 execution/stage. (注意:在请求令牌时,我提供了一个回调 URL,它将在用户登录时联系。)
- 我的前端从第 2 步收到 url 并将用户重定向到那里登录。
- 用户通过该登录批准,并调用回调 URL。这包括验证器。然后我必须使用步骤 2 中的验证器和令牌和秘密获取 accessToken。虽然我认为这些将在 session 中检索,但它们都是未定义的,因此我对 accessToken 的请求失败了。
在我看来,每次调用都会创建一个新的 session,无论是否已经存在 session。如果我在第 1 步和第 2 步检查 session,它们的时间戳是不同的,这让我认为第 2 步不是从第 1 步检索 session,而是创建一个新的——很可能是因为我的表现 app.use(session)
.
我使用的技术列表:
- node.js
- 快递
- express-session
- firebase-functions
- connect-session-firebase(也尝试过 firestore-store,对下面的代码进行非常轻微的调整以匹配其基本设置。)
这是我的后端实现:
const url = require('url');
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const express = require("express");
const app = express();
const cors = require("cors")
const session = require('express-session')
const FirebaseStore = require('connect-session-firebase')(session);
// Etsy OAuth
// For Etsy OAuth
const etsyjs = require('etsy-js');
const client = etsyjs.client({
key: process.env.ETSY_KEY,
secret: process.env.ESTY_SECRET,
callbackURL: '<location>/authorize'
});
const ref = admin.initializeApp({
credential: admin.credential.cert(json), //json retrieved and put here
databaseURL: 'https://<app>.firebaseio.com'
});
app.use(cors())
app.use(session({
store: new FirebaseStore({
database: ref.database(),
}),
secret: 'My secret',
resave: true,
saveUninitialized: true,
name: '__session',
cookie: {
maxAge : 60000,
secure: false,
httpOnly: false
}
}
));
// Step 1 for backend of OAuth
app.get('/begin-oauth', (req, res) => {
res.setHeader('Cache-Control', 'private'); // I read somewhere this needs to be set, but I am not seeing a difference either way with it.
return client.requestToken((err, response) => {
if (err) {
return console.log(err);
}
req.session.token = response.token;
req.session.sec = response.tokenSecret;
res.status('200').send(response) // returning url to send the user there via frontend.
});
});
// Step 2 for backend of OAuth
app.get('/authorize', (req, res) => {
let query, verifier;
query = url.parse(req.url, true).query;
verifier = query.oauth_verifier;
// session at this point should have token and sec stored from before but are not present.
return client.accessToken(req.session.token, req.session.sec, verifier, (err, response) => {
if (err) {
console.log(err);
}
req.session.token = response.token;
req.session.sec = response.tokenSecret;
// Finish up.
});
});
好的,现在一切正常。有几件事让它发挥作用。
- 在后端调用中包含凭据,现在看起来像这样:
return await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
}).then(...)
- 正在将凭据添加到 cors 设置 server-side。所以现在看起来像这样:
var corsOptions = {
credentials: true,
origin: true
}
app.use(cors(corsOptions))
- 这些仍然没有完全解决问题。最终解决问题的是我从浏览器中清除了缓存。我相信这是真正解决问题的方法!尝试这个简单的事情花了多长时间绝对是痛苦的。
更新:
所以上面修复了开发环境中的所有内容,但当我实际部署到 Google 函数时却没有。那时我遇到了一系列围绕 cors 的新问题。这是我收到的错误消息:这是我为解决此问题所做的更改:
app.use(function(req, res, next) {
var allowedOrigins = ['http://localhost:3000', 'http://localhost:5000', 'https://www.<site>.com', 'https://<app-location>.cloudfunctions.net', 'https://<app>.firebaseio.com']
var origin = req.headers.origin;
functions.logger.log('Checking headers:')
functions.logger.log(origin)
functions.logger.log(req.headers)
if(allowedOrigins.indexOf(origin) > -1){
res.setHeader('Access-Control-Allow-Origin', origin);
}
res.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Credentials', true);
return next();
});
并且我将 cookie 更新为:
cookie: {
maxAge: 30*24*60*60*1000,
secure: true,
httpOnly: false,
sameSite: 'none'
}
^ 特别是,我设置了 secure: true
和 sameSite: 'none'