沒想到看似簡單的問題也折騰了二天,通常 debug 這種問題常常會讓人煩燥,但也是訓練耐心的一個機會,希望遇到類似問題的網友,看到這篇能夠迎刃而解
環境說明
-
AWS API Gateway 設定 Resources path 時,使用 {proxy+} 來 proxy 到 lambda
-
Lambda 使用 express-session 來處理 session 和 cookie,也是本篇要講的重點
問題概述
express-session github 上的 options 說明其實很清楚,原先設定如下
app.use(session({
cookie: {
domain: config.CUSTOM_DOMAIN_NAME,
expires: new Date(Date.now() + config.SESSION_COOKIE_MAX_AGE_MS),
httpOnly: true,
sameSite: true,
secure: true,
},
name: config.SESSION_NAME,
resave: false,
saveUninitialized: false,
secret: config.SESSION_SECRET,
store: createSessionStore(), //using DyanmoDB
}))
然後這樣的設定,會發現 session 存在 DynamoDB 是沒問題,也都有看到 session data,但 Browser 上的 cookie 總是看不到,而 Browser 要設定 cookie ,重點在於 Response Headers 必需要有 set-cookie 這個 header,cookie 才能儲存正常
除錯經過
追了一下 express-session github 上的 code,在 function issecure 中的判斷擋掉了,如下
if (trustProxy !== true) {
return req.secure === true
}
而 trustProxy 就是在 options 中的 proxy,因為沒設定,所以預設會進這段判斷,而 AWS API Gateway proxy 到 Lambda 的 req.secure 是 false,這段其實滿奇怪,明明 AWS API Gateway endpoint 已經是 https,就算用 Custom domain 也是一樣
所以找到這行問題後,只要把 proxy 參數加上去如下
app.use(session({
cookie: {
domain: config.CUSTOM_DOMAIN_NAME,
expires: new Date(Date.now() + config.SESSION_COOKIE_MAX_AGE_MS),
httpOnly: true,
sameSite: true,
secure: true,
},
name: config.SESSION_NAME,
resave: false,
proxy: true, // 這行是重點
saveUninitialized: false,
secret: config.SESSION_SECRET,
store: createSessionStore(), //using DyanmoDB
}))
這樣設定會正常是因為繞開剛才的判斷後,express-session 會用 header x-forwarded-proto 來檢查是否為安全的連線,而 {proxy+} 的設定會 pass 這個 header 過來,所以就一切正常了
補充
API Gateway 如果掛在 CloudFront 後時,express 的 req.secure 就會是 true,也不會踩到這個雷了