TL;DR:
Express、Express-Session、および Express-Cors を使用して、バックボーン アプリと Node.js サーバー間で実行される複数の API 呼び出しにわたるセッションの永続化を成功させることができません。呼び出しのたびにセッションが再初期化/失われたようです。
長いバージョン:
localhost:3000
で実行されている Node.js サーバーで次の呼び出しを実行するクライアント Backbone/React/Flux アプリケーションを実行していlocalhost:4242
ます。
HTTP 呼び出し
RESPONSE HEADERS
Content-Type application/json; charset=utf-8
Set-Cookie connect.sid=s%3AFeNYY5GQGvkyRvOym7DhysprePaQr7xP.BrxOPP56k9pDpxQPvwjDFaxkEYoHU%2FAEtNUIXGltqjI; Domain=http://localhost:3000; Path=/
Vary Origin
X-Powered-By Express
access-control-allow-credentials true
access-control-allow-orign http://localhost:3000
[...]
REQUEST HEADERS
Accept application/json, text/javascript, */*; q=0.01
Content-Type application/json; charset=utf-8
Cookie connect.sid=s%3AjP4iADZvRnDHbJcCE8H81Zy6TIehj9BJ.eDOTw44GcHqq8i2dslBGd43tXMQ22ENl31fRizdC8iA
Host localhost:4242
Origin http://localhost:3000
Referer http://localhost:3000/login
[...]
RESPONSE HEADERS
Content-Type application/json; charset=utf-8
Set-Cookie connect.sid=s%3ARxf91_vLMBqzB6xN-0QFIIk_-SyBP9_8.F1Mr%2BVSkYNJ6MqnzO%2BsxxfwXRinIX6th80SoukG1QBM;Domain=http://localhost:3000; Path=/
Vary Origin
X-Powered-By Express
access-control-allow-credentials true
access-control-allow-orign http://localhost:3000
[...]
REQUEST HEADERS
Accept application/json, text/javascript, */*; q=0.01
Content-Type application/json; charset=utf-8
Cookie connect.sid=s%3AjP4iADZvRnDHbJcCE8H81Zy6TIehj9BJ.eDOTw44GcHqq8i2dslBGd43tXMQ22ENl31fRizdC8iA
Host localhost:4242
Origin http://localhost:3000
Referer http://localhost:3000/login
[...]
基本的に、最初の呼び出しPOST /api/session
はユーザーのログインであり、API トークンをセッションに保存しようとします。2 番目の呼び出しGET /api/users
は、最初の呼び出しが成功した直後にトリガーされ、ユーザー情報を取得します。
バックボーン方式
ログイン用のセッションモデルのバックボーンメソッドは次のとおりです。
login: (options) ->
@set {user: options.user, password: options.password}
@save ['user', 'password'],
success: (data) =>
@set({authenticated: true, accessToken: data.accessToken, password: null})
options.success(data) # trigger the second call here
error: (error) =>
options.error(error)
そして、UserStore の /api/users への呼び出し
users: (options) ->
@users.fetch
success: (users) =>
@users = users
options.success(users)
これらのさまざまなオプションを使用します (Backbone.Collection/Backbone.Model で Backbone.sync をオーバーライドしました):
class UsersCollection extends Backbone.Collection
url: '/api/users'
model: UserModel
sync: (method, model, options) ->
options ?= {}
options.url ?= @url
options.dataType ?= 'json'
options.contentType ?= "application/json; charset=utf-8"
options.crossDomain ?= true
options.xhrFields ?= {"withCredentials": true}
super(method, model, options)
(簡略化されたバージョン: BaseCollection と BaseModel を使用して、sync() メソッドをオーバーライドしているモデルとコレクションの両方で同じこと)。
そのため、Console.log(options)
inBackbone.sync(method, model, options)
が返されます:
{"url":"http://localhost:4242/api/session","dataType":"json","contentType":"application/json; charset=utf-8","crossDomain":true,"validate":true,"parse":true,"xhrFields":{"withCredentials":true}}
Node.js のセットアップとメソッド
これが私の Node.js ルーター設定 Express です:
BodyParser = require 'body-parser'
Session = require 'express-session'
Cors = require 'cors'
class Router
constructor: (express) ->
@express = express
@express.use BodyParser.json()
@express.use Cors(@corsConfig())
@express.use Session(@sessionConfig())
# Express routes are set here
# @express.post '/api/session', (request, response) => [...]
# @express.get '/api/users', (request, response) => [...]
corsConfig: ->
origin: 'http://localhost:3000'
credentials: true
sessionConfig: ->
secret: 'whatever'
cookie:
secure: false
httpOnly: false
domain: 'http://localhost:3000'
これが私のNode.jsメソッドの処理ですPOST /api/session
login: (request, response) ->
session = request.session
console.log JSON.stringify(session)
console.log request.sessionID
console.log '---------------------------------------------'
if session.accessToken
console.log 'session with token!'
response.json {accessToken: session.accessToken}
else
console.log 'performing credentialAuthentication'
user = request.body.user
password = request.body.password
@whatever.authentication
user: user
password: password
success: (accessToken) ->
request.session.accessToken = accessToken
console.log JSON.stringify(session)
console.log request.sessionID
console.log '---------------------------------------------!!!'
response.json {accessToken: accessToken}
# also tried with response.send()
そしてお取り扱いの方GET /api/users
@express.get '/api/users', (request, response) =>
console.log JSON.stringify(request.session)
console.log request.sessionID
console.log '---------------------------------------------'
[...]
Node.js ログ
ログは次のとおりです。
express:router dispatching OPTIONS /api/session
express:router dispatching POST /api/session
{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":false,"path":"/"}}
zse18d2zrNRdEXPjFHF0gm3NkONb-_5V
---------------------------------------------
performing credentialAuthentication
{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":false,"path":"/"},
"accessToken":"ebab5010f9ece5ea984e4b73f9a46ef3"}
zse18d2zrNRdEXPjFHF0gm3NkONb-_5V
---------------------------------------------!!!
express:router dispatching GET /api/users
{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":false,"path":"/"}}
g8YXQEpt_rnWSGdh1nCKMndiI8Lt2UDq
---------------------------------------------
CORS リクエストが正常に実行されていることがわかるように、トークンを適切に取得し、それをセッション内に保存しようとしています。
ただし、2 番目の呼び出しではセッションが永続化されず、最初の呼び出しで実際に設定している変数 (accessToken) にアクセスできません。
ログと 2 つの呼び出しの HTTP ヘッダーを見ると、セッション ID が変更され、各要求が行われるため、毎回セッションが再初期化されているように見えます。私が理解している限りではそうです)。
この動作は、CORS レベルでの構成の一貫性がないか、欠落していることが原因であるか、またはpath
セッション ( Express.use(path, middleware)
、およびCookie({path: '/'})
) のセットが原因であると思われます。ただし、さまざまな構成、セットアップ、およびヘッダーを何度も試みたにもかかわらず、実際には機能しません。
この振る舞いと私が欠けているものについて私を啓発できる人は誰でも大歓迎です:)
ありがとう!
PS: CoffeeScript を使用していない開発者には申し訳ありません ;)