如何确定我在哪个会话上进行 Meteor 中的 OAuth 重新身份验证

How to determine which session I'm on for OAuth reauthentication in Meteor?

本文关键字:中的 Meteor OAuth 身份验证 何确定 会话      更新时间:2023-09-26

我已经编写了自己的自定义身份验证来使用我们的企业OAuth解决方案。 这会导致向我发送 OAuth 访问和刷新令牌。然后,我可以将这些令牌存储在 Meteor 数据库的用户集合中,但是当我想执行重新身份验证时,我需要能够找到正确的会话,以便能够找到我应该使用哪个 OAuth 令牌来刷新它(如果需要(。 由于用户可以从多个设备登录,这使事情复杂化。

这是我用来存储令牌并在服务器端发送结果的代码:

            var userId = null;
            var user = Meteor.users.findOne({username: userName});
            if (!user) {
                userId = Meteor.users.insert({username: userName});
            } else {
                userId = user._id;
            }
            logger.info("User logged in: " + userId);
            var initToken = Accounts._generateStampedLoginToken();
            var token = Accounts._hashStampedToken(initToken);
            token.accessToken = result.data.access_token;
            token.refreshToken = result.data.refresh_token;
            token.ttl = result.data.expires_in;
            // Need way to bind oath.loginTokens with Meteor resume token
            Meteor.users.update(userId,
                {$push: {'services.oauth.loginTokens': token}}
            );
            var rslt = {
                userId: userId
            };
            return(rslt);

这是数据库中的结果记录:

"services" : {
    "oauth" : {
        "loginTokens" : [ 
            {
                "when" : ISODate("2014-06-17T17:51:24.635Z"),
                "hashedToken" : "ErcosEo9rD+IuT3EyFb3DFS8Bf0enwLzkCIf/nP1JFE=",
                "accessToken" : "bhafr3WBDS67EmZ9hFE20af83BJRPFQQS8NGpMlSH6NHVCOiTeTuTJ",
                "refreshToken" : "enOAFkBcxB88FlATUh2m0E5NLLG0y8AojyIH5gItnJXdU6",
                "ttl" : 3600
            }
        ]
    },
    "resume" : {
        "loginTokens" : [ 
            {
                "when" : ISODate("2014-06-17T17:51:24.637Z"),
                "hashedToken" : "uhRZpGdBHnAVKvgBEm7oSWsdflOGRI2YrR9Q21iqjzp+Xc="
            }
        ]
    }
},
"username" : "lous"

从上面可以看到,我需要关闭其中一个令牌值,以找到正确的 oauth 信息以进行可能的刷新。在客户端,然后我执行如下所示的操作,但问题是 validateResult 中返回的令牌与存储在数据库中的令牌不同,因此我无法跟踪哪个会话是我的。

Template.login.events({
    'submit #login-form': function(e,t) {
        e.preventDefault();
        var id = t.find('#login-id').value,
            password = t.find('#login-password').value;
        var req = {id: id, password: password};
        Accounts.callLoginMethod({
            methodArguments: [req],
            validateResult: function (result) {
                var token = result.token;
                window.localStorage.setItem('token', token);
                subscribeToRequests();
                $.mobile.changePage('#landingPage', {transition: 'slidefade'});
            },
            userCallback: function(error) {
                if (error) {
                    console.log("Error: " + error.message);
                    alert("Login Failure");
                } 
            }
        });
        return false;
    }
});

为什么令牌不一样?关于如何解决这个问题的任何建议? 那么,一旦我确实在客户端存储了令牌,Meteor 是否提供了一种开箱即用的方式来测试令牌的有效性? 这是我用来尝试这样做的findUser方法:

Meteor.methods({
    findUser: function(token) {
        var user = null;
        var hashedToken = Accounts._hashLoginToken(token);
        if (this.userId) {
            //TODO need user object to include token to do TTL check and reauth if necessary
            user = Meteor.users.findOne({_id:this.userId});
            var result = refreshUser(user);
            if (result.err) {
                throw { name: 'System Error', message: 'The following error occurred: ' + result.err
                };
            }
        } else {
            throw { name: 'System Error', message: 'No userId available. Please try again.'
            };
        }
        return user;
    }
});

findUser 方法中,可以调用 Accounts._getLoginToken(this.connection.id) 来获取连接的当前登录令牌。然后,您可以查找与此值关联的 OAuth 访问令牌。

至于您关于为什么客户端上的result.token与数据库中的不同的原始问题:我认为您正在将未散列令牌(result.token(与存储在数据库中的散列令牌进行比较。如果是这种情况,您可以将result.token传递给服务器并通过Accounts._hashLoginToken传递,然后再在数据库中查找它。

这有意义吗?

以下是我最终如何将我的OAuth令牌与Meteor会话令牌对齐的方式。

我在服务器端创建了以下Meteor.methods,我的客户端只是在我的Accounts.callLoginMethod validateResult调用updateToken,因此可以通过@emily答案中描述的方法找到我的 oauth 令牌。

每当应用程序启动或刷新时,它都会调用reauthenticateUser,最后,当用户记录我们的或会话超时到期时,它会调用logoutUser

updateToken: function() {
    // Update oauth hash token with correct hashed token
    var user = Meteor.users.findOne({'_id': this.userId});
    var hashedToken = Accounts._getLoginToken(this.connection.id);
    // Get last element for OAuth array (safely presuming the last one is the last oauth from current call stack) and update hashedToken
    var oauthObj = _.last(user.services.oauth.loginTokens);
    Meteor.users.update({'services.oauth.loginTokens': {$elemMatch: {hashedToken: oauthObj.hashedToken}}}, {$set: {'services.oauth.loginTokens.$.hashedToken': hashedToken}});
},
reauthenticateUser: function() {
    var user = null;
    if (this.userId) {
        var hashedToken = Accounts._getLoginToken(this.connection.id);
        user = Meteor.users.findOne({$and: [{'_id': this.userId}, {'services.oauth.loginTokens': {$elemMatch: {hashedToken: hashedToken}}}]});
        // Get specific oauthTokens (keep in mind multiples per client)
        var oauthTokens = _.findWhere(user.services.oauth.loginTokens, {'hashedToken': hashedToken});
        var result = refreshUser(this.userId, user.username, oauthTokens);
        if (result.err) {
            throw { name: 'System Error', message: 'The following error occurred: ' + result.err
            };
        }
    } else {
        throw { name: 'System Error', message: 'No userId available. Please try again.'
        };
    }
    return user;
},
logoutUser: function() {
    var hashedToken = Accounts._getLoginToken(this.connection.id);
    // Remove orphaned Oauth tokens
    Meteor.users.update(
        {$and: [{'_id': this.userId}, {'services.oauth.loginTokens': {$elemMatch: {hashedToken: hashedToken}}}]},
        {$pull: {'services.oauth.loginTokens': {'hashedToken':hashedToken}
        }});
    return true;
}

一旦我就位了,我很容易在刷新 oauth 令牌后更新它们,或者在用户注销后删除它们。

我不是meteorJS开发人员,但我会尝试建议解决问题的方法。

使用 npm 安装 meteor-cookie 安装 meteor-cookie。
然后:

        var initToken = Cookie.get('initToken');
        if(!initToken) {
            initToken = Accounts._generateStampedLoginToken();
            Cookie.set('initToken', initToken, {days: 30});
        }
        var token = Accounts._hashStampedToken(initToken);
        token.accessToken = result.data.access_token;
        token.refreshToken = result.data.refresh_token;
        token.ttl = result.data.expires_in;

或:

        var token = Cookie.get('token');
        if(!token) {
            var initToken = Accounts._generateStampedLoginToken();
            token = Accounts._hashStampedToken(initToken);
            Cookie.set('token', token, {days: 30});
        }
        token.accessToken = result.data.access_token;
        token.refreshToken = result.data.refresh_token;
        token.ttl = result.data.expires_in;

也许我的添加中有错误,但我认为您已经理解了诀窍。(: