1
votes

I am trying to login to single user with multi OAuth (facebook, google) login service. Here is what I try. In Client:

'click #signInByFacebook': function (e) {
    e.preventDefault();
    Meteor.loginWithFacebook({requestPermissions: ['public_profile', 'email', 'user_about_me', 'user_photos']}, function (err, res) {
        if (err) {
            showError($('.alert'), err, 'login');
            return;
        }
        showSuccess($('.alert'), 'login');
        Session.set('notAdmin', !Roles.userIsInRole(Meteor.user(), ["admin"]));
        Router.go('/');
    });
},
'click #signInByGoogle': function (e) {
    e.preventDefault();
    Meteor.loginWithGoogle({requestPermissions: ['profile', 'email', 'openid']}, function (err, res) {
        if (err) {
            showError($('.alert'), err, 'login');
            return;
        }
        showSuccess($('.alert'), 'login');
        Session.set('notAdmin', !Roles.userIsInRole(Meteor.user(), ["admin"]));
        Router.go('/');
    });
}

In Server:

Accounts.onCreateUser(function (options, user) {
    if (options.profile) {
        user.profile = options.profile;
    }
    var sameuser = Meteor.users.findOne({$or: [{'emails.address': getEmail(user)}, {'services.facebook.email': getEmail(user)}, {'services.google.email': getEmail(user)}]});
    console.log(sameuser);
    if (sameuser) {
        if (user.services.facebook) {
            console.log("facebook");
             Meteor.users.update({_id: sameuser._id}, {$set: {'services.facebook': user.services.facebook}});
        }
        if (user.services.google) {
            console.log("google");
            Meteor.users.update({_id: sameuser._id}, {$set: {'services.google': user.services.google}});
        }
        return;
    }
    console.log('register success');
    return user;
});

This code will check if any user logined with facebook/google has the same email or not with current sign in. If they are the same, just update information to old account. If not, create new user.

This works great, but there is a problem with the 'return ;' in server code. I dont know what should I return to stop create user and auto login to the user that has same email. Anybody can help this issue ? Thank you.

1
See related (if not duplicate) question Prevent Account Creation for loggedIn Users Meteorjs - Michel Floyd

1 Answers

1
votes

The only way to stop creation of the new user is to throw an exception, but that will also prevent logging in as the existing user.

However, your general approach is insecure. Consider a user who has a Google account with a strong password and a Facebook account with a weak one. When he uses the Google account to authenticate with your app, he doesn't (and shouldn't) expect that someone who gains access to his Facebook account will be able access your app as him.

A better approach is to require that the user be logged into both services simultaneously before merging the services. The good news is that this also means that you don't need to worry about logging in after preventing the creation of the new user, because the user will already be logged in. Something like this might work:

Accounts.onCreateUser(function (options, user) {
    if (options.profile) {
        user.profile = options.profile;
    }
    var currentUser = Meteor.user();
    console.log(currentUser);
    if (currentUser) {
        if (user.services.facebook) {
            console.log("facebook");
             Meteor.users.update({_id: currentUser._id}, {$set: {'services.facebook': user.services.facebook}});
        }
        if (user.services.google) {
            console.log("google");
            Meteor.users.update({_id: currentUser._id}, {$set: {'services.google': user.services.google}});
        }
        throw new Meteor.Error(Accounts.LoginCancelledError.numericError, "Service added to existing user (or something similar)");;
    }
    console.log('register success');
    return user;
});

There are still a couple loose ends. First, I think Meteor expects OAuth credentials to be "pinned" to the user that they are associated with, so you probably need to repin the credentials you are copying.

Second, the above approach bypasses the validateLoginAttempt() callbacks. If you, or any package you are using, has registered any such callbacks, they won't be called when logging in using the second service, so they won't be able to prevent any such logins that they might consider invalid.

You can address both of these issues and skip the onCreateUser() callback as well, by just adding my brettle:accounts-add-service package to your app.