2013-01-15 22 views
4

我正在嘗試使用node-openid(通過passport-google)通過他們的Google憑據對我的用戶進行身份驗證。它在我的開發機器上工作正常,但是當我用2個dynos將它部署到Heroku時,它在一個dyno處理整個OpenID對話時起作用,並且當對話在一個dyno上開始並在第二個時完成時失敗。在這種情況下,我得到以下錯誤:如何在Heroku上用多個dyno使用node-openid?

2013-01-15T15:18:24+00:00 app[web.2]: Failed to verify assertion (message: Invalid association handle) 
2013-01-15T15:18:24+00:00 app[web.2]:  at Strategy.authenticate.identifier (/app/node_modules/passport-google/node_modules/passport-openid/lib/passport-openid/strategy.js:143:36) 
... 

什麼是處理這個問題的正確方法?我是否應該將對話狀態保存在數據庫中,以便兩個dynos都可以訪問它?

更新:

下面是我使用的存儲MongoDB中的ASSOCATION解決問題的代碼。

var 
    GoogleStrategy = require('passport-google').Strategy; 

// We have to save the OpenID state in the database so it's available to both 
// dynos. 

db.collection('OpenID').ensureIndex({expires: 1}, {expireAfterSeconds: 0}, 
    function(err, result) { 
     if (err) { 
      throw new Error('Error setting TTL index on OpenID collection.'); 
     } 
    }); 

// Use the GoogleStrategy within Passport. 
// Strategies in passport require a `validate` function, which accept 
// credentials (in this case, an OpenID identifier and profile), and invoke a 
// callback with a user object. 
strategy = new GoogleStrategy({ 
    returnURL: 'http://localhost:3000/auth/google/return', 
    realm: 'http://localhost:3000/' 
    }, 
    function(identifier, profile, done) { 
    // asynchronous verification, for effect... 
    process.nextTick(function() { 

     // To keep the example simple, the user's Google profile is returned to 
     // represent the logged-in user. In a typical application, you would want 
     // to associate the Google account with a user record in your database, 
     // and return that user instead. 
     profile.identifier = identifier; 
     return done(null, profile); 
    }); 
    } 
); 

strategy.saveAssociation(function(handle, provider, algorithm, secret, expiresIn, done) { 
    db.collection("OpenID").insert({ 
     handle: handle, 
     provider: provider, 
     algorithm: algorithm, 
     secret: secret, 
     expires: new Date(Date.now() + 1000 * expiresIn) 
    }, done); 
}); 

strategy.loadAssociation(function(handle, done) { 
    db.collection("OpenID").findOne({handle: handle}, function (error, result) { 
     if (error) 
      return done(error); 
     else 
      return done(null, result.provider, result.algorithm, result.secret); 
    }); 
}); 
+0

你能提供一些關於你如何解決這個問題的細節嗎?我實際上使用一些中間件(passport-google)來執行此身份驗證。 Passport設置爲使用Redis商店進行會話。你能讓我知道你必須做什麼才能做到這一點? – electrichead

+0

@electrichead,我在上面添加了我的代碼。 –

+0

@DavidBraun您如何設置'saveAssocation' /'loadAssociation'?在這種情況下,「策略」是什麼? – kynan

回答

5

你或許應該看看在節點的OpenID README的Storing association state部分。默認情況下,會話狀態存儲在內存中的各個dynos上,這就是導致問題的原因。

覆蓋saveAssociation()loadAssociation() mixins以使用應用程序當前正在使用的任何後備存儲。還有更多documentation in the passport-openid source code

+0

謝謝,你釘了它。我的應用程序正在工作。 –