具有“持久化令牌”功能的Node.js身份验证库

6
我已掌握Node中的passport,但它没有以下功能:
- 生成“持久性令牌”(例如在authlogic/session/session.rb#L35中) - 生成易失性令牌以进行密码重置 - 记住我功能 - 在某些模型类上管理登录/注销的这些属性等
在Node.js社区中是否有解决此问题的库?如果有像Rails的Devise那样强大(或正在变得强大)的库,那就太完美了,但任何解决此令牌问题的库都可以。
令人惊讶的是,很多示例将用户id存储在会话中!
request.session['userId'] = user.get('id')

那只要求被黑客攻击。应该改为这样:
require.session['persistenceToken'] = App.User.generateRandomToken()

我猜实现起来并不难,只是希望它已经存在了。 - Lance
我已经制定了一个“记住我”的策略:https://github.com/jaredhanson/passport-remember-me - Jared Hanson
3个回答

8
一次性密码(也称为单次使用令牌)策略用于密码重置,我将实施该策略。鉴于Passport的架构,这可以轻松成为一个单独的模块,而且不会让我惊讶地发现其他人已经实现了这样的事情。
记住我和持久化令牌功能也是我想支持的。最好,我希望它也是一个单独的策略,但它可能需要一些核心支持。如果确实是这种情况,那么在req.logIn中加入几行额外的代码就应该可以解决它。
至于在会话中存储用户ID,我认为没有任何大风险,因为默认情况下,在Connect / Express中,会话属性完全存储在后端,并由设置在加密cookie中的唯一sid查找。恶意用户必须同时拥有唯一的sid和会话秘密才能欺骗请求。
Mozilla正在使用Passport作为其身份验证努力的一部分,以桥接BrowserID到其他缺乏BrowserID支持的提供商(参见browserid-bigtent)。考虑到他们的要求,开发人员可以确信Passport符合严格的安全要求。
(最终,在Passport中的会话序列化是应用程序的责任,因此出于安全原因,可以使用随机令牌代替用户ID。显然,如果直接在cookie中存储数据,则应该这样做,但我建议这是最不明智的方法。)
就管理模型上的这些属性而言,Passport旨在完全独立于模型/ORM。我不打算改变这个事实,因为我认为这些决策最好留给应用程序(并且通过委派此责任,Passport更加灵活)。尽管如此,我认为有其他模块可以独立地基于Passport构建以提供此功能。
总之,我认为Passport是现有Node.js身份验证解决方案中最强大的解决方案。前三个请求将极大地促进其实现,而且它们应该很容易实现。我很乐意合作,以使这些功能得到实现,因此请随时与我联系。
最后,如果有人好奇的话,一流的API身份验证正在进行中,位于authinfo分支上。基于此,passport-http-oauth实现了OAuth服务器策略,可以与oauthorize中间件组合作为工具包来组装OAuth服务器。虽然这还没有完全完成,但在它准备好之后,它将成为Passport的又一个有效功能。

我正在使用本地认证策略的passport,并希望添加“记住我”功能。我是否可以在我的serializeUser()函数中设置cookie生存期,还是必须修改核心代码?或者有其他方法吗? - ragulka

2
与此同时,以下内容应该足够了。根据您的应用程序和所需的 cookie 持续时间进行调整。检查用户名和密码是一种有点低劣的检测登录尝试的方式,但与 passport 配合使用效果还不错。
app.use(function(req, res, next) {
    if(
            typeof(req.body.username) !== "undefined"
            && typeof(req.body.password) !== "undefined"
            ) {
        if(req.body.remember == 1) {
            req.session.cookie.maxAge = 365*24*60*60*1000;
        }
        else {
            req.session.cookie.maxAge = 24*60*60*1000;
        }
    }
    next();
});

我想知道为什么在 else 语句中设置 res.session.cookie.maxAge = null; 不起作用?我尝试过了,如果我选择了“记住我”的选项并进行了一次登录,那么所有后续的登录都会设置一个持久性 cookie,即使“记住我”处于关闭状态。 - ragulka
我刚意识到我应该使用 req.session.cookie.expires = false,我会发布一个替代答案来回答这个问题。 - ragulka

2

虽然我确实希望在passport.js中看到这些功能,但它们还没有出现。

我创建了一个简单的随机令牌生成器,用于与passport.js的serializeUser()函数一起使用,并稍微修改了Justen的答案以适应我的需求。基本上,唯一的区别是,如果未设置“remember”选项,则会话将持续到浏览器关闭。

这是我的序列化程序,其中包括随机访问令牌生成器。我正在使用Mongodb和Mongoose,但是该实现应该很好地转换到其他系统中。

基本上,我获取时间并将一个随机的16个字符的字符串附加到它后面。然后,在serializeUser()函数中,我检查是否没有其他用户具有相同的令牌(令牌应该是唯一的!)。

User.methods.generateRandomToken = function () {
  var user = this,
      chars = "_!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
      token = new Date().getTime() + '_';
  for ( var x = 0; x < 16; x++ ) {
    var i = Math.floor( Math.random() * 62 );
    token += chars.charAt( i );
  }
  return token;
}; 

这是序列化器:

passport.serializeUser( function ( user, done ) {

  var createAccessToken = function () {
    var token = user.generateRandomToken();
    app.User.findOne( { accessToken: token }, function (err, existingUser) {
      if (err) return done( err );
      if (existingUser)
        createAccessToken(); // Run the function again - the token has to be unique!
      else {
        user.set( 'accessToken', token );
        user.save( function ( err ) {
          if (err) return done( err );
          return done( null, user.get('accessToken') );
        })
      }
    });
  };

  if ( user._id ) {
    createAccessToken();
  }
});

这是处理“记住我”功能的中间件,以下是我的版本。不过,我更希望它能成为serializeUser函数或passport.js核心的一部分。

app.use( express.session( { secret: 'secret_key' } ) );
app.use( function (req, res, next) {
    if ( req.method == 'POST' && req.url == '/login' ) {
      if ( req.body.remember ) {
        req.session.cookie.maxAge = 30*24*60*60*1000; // Rememeber 'me' for 30 days
      } else {
        req.session.cookie.expires = false;
      }
    }
    next();
});
app.use( passport.initialize() );
app.use( passport.session() );

我希望这样能对您有所帮助。我花了几个小时才弄清楚,我不确定这是否是最好的方法,但目前它对我有效。


我知道这很老了,但在这里使用会话中间件有什么好处吗?我们不能跳过它吗? - KGo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接