Make User register natively after 3rd party login meteor

I’m trying to make an application that will allow for the user to log in via their 3rd party login or the native login system after they register their 3rd party service account.
For every registration with a 3rd party profile, the application should create a accounts profile like it would with a regular creation of a user.
This will also allow me to get the user to agree to our terms of service and subscribe to our mailing list.

I considered multiple ways of doing this. Im using two accounts packages eahefnawy:accounts-coinbase, and accounts-google. In my current system I’m trying out making the user reconfirm their information provided by the OAuth login, and then combine the info to the account made with all the OAuth passed info (SInce when I log in with a 3rd party application for the first time it creates an account with all the info). I believe this would be similar to Meteor Link Accounts or Meteor Accounts Meld, but I can’t exactly understand how to integrate it with what I have. Also, they currently don’t have support for eahefnawy:accounts-coinbase, the Coinbase login package I’m using. This is how I am doing it currently:

I have currently encountered problems with updating the info on the account. When I update the info it erases the previous info of the document when using Meteor.users.update. I guess I need something like merge info.

Ill give you a little background how Im making this work. Everything other than the last update problem is working.

Made a system to see where the user should be redirected if they have not completed their account setup
I assumed service config would work with password as that is how it is stored in regular meteor accounts.

function logValue(){
    var profileConfig = ServiceConfiguration.configurations.findOne({service: 'password'});
    var coinConfig = ServiceConfiguration.configurations.findOne({service: 'coinbase'});
    var googleConfig = ServiceConfiguration.configurations.findOne({service: 'google'});
    if (!Meteor.user())
        return 0;
    else if (coinConfig && !profileConfig)
        return 1;
    else if (googleConfig && !profileConfig)
        return 2;
    else
        return 3;
}


// This is a substitute for {{#if currentUser}}
Template.registerHelper('currentProfile', function(){
    if (logValue()!==3)
        return false;
    else
        return true;
});

So after the login with the 3rd party service the user is sent to finish their registration. This registration consists of a filled form where the user confirms their info passed by the 3rd party service, sets a user password and the agrees to the terms. I use this form submission to update the info:

'submit form': function(event,template){
        event.preventDefault();
        var nameVar = template.find('#name').value;
        var emailVar = template.find('#email').value;
        var passVar = template.find('#password').value;
        const info = {
            id: Meteor.userId(),
            prof: {
                $set: {
                    emails : [
                        {
                            address : emailVar,
                            verified : false,
                        }
                    ],
                    profile : {
                        name : nameVar,
                    }
                },
            },
            pass: passVar,
        };
        Meteor.call('updateUser', (info),
            (err) => {
                if (err) {
                    alert(err);
                }
            }
        );
    }

This is the server method that I call to update the User and where I’m having some problems:

updateUser: (info) => {
        var pass = passHash(info.pass);
        Meteor.users.update({_id: Meteor.userId()},info.prof);
        Meteor.users.update({_id: Meteor.userId()},
            {
                $set: {
                    services: {
                        password: {
                            bcrypt: pass,
                        },
                    },
                }
            }
        );
    },

Im inserting the info it manually as I thought it would stop erasing the data. I was having the same issues with the Meteor account methods such as set password below. That is why I was manually hashing the password with bcrypt.

Accounts.setPassword(userId, newPassword, [options])

It was erasing the info passed by the OAuth login. I assume this way would have no issues as I followed the same structure required for regular account logins.

This is what the account info looks like in the Mongo Document:

// This is right before I update the information. The account made with the coinbase login similar to other
// packages like accounts-facebook, and accounts-google 
{
    "_id" : "5dNYmJp9APCgxi8se",
    "createdAt" : ISODate("2018-06-15T21:14:38.276Z"),
    "services" : {
        "coinbase" : {
            COINBASE PROVIDED INFO       },
        "resume" : {
            "loginTokens" : [ 
                {
                    LOGIN STUFF
                }
            ]
        }
    },
    "profile" : {
       COINBASE MADE PROFILE
    }
}

//This is what happens when I update an account with my registration form submission.

{
    "_id" : "fxTu4YuhBsAvz2Gey",
    "createdAt" : ISODate("2018-06-15T21:21:32.710Z"),
    "services" : {
        "password" : {some hashed password havnt gotten that working yet}
    },
    "profile" : {
        "name" : "New Reconfimed and updated info"
    },
    "emails" : [ 
        {
            "address" : "sdfsdfsd@sdfsd",
            "verified" : false
        }
    ]
}

As you can see the coinbase info has been erased from services when I update the info. I am currently looking for a solution for how to not erase that coinbase info. I also believe there might entirely be a better way of achieving the same goal and in a simpler way. I am completely open to suggestions.

This is also something I was looking into, but could not understand how to integrate:

I’m not quite sure if I entirely understand the problem.

So you have 3 options to sign up:

  • via email and password
  • via coinbase
  • via google

In any case you want to ask for common data the user has to provide before he can use your app. This includes terms to be accepted.

If I got that right, this is exactly the same scenario I have implemented in my app.

I think it’s pretty simple. I don’t exactly get what you are doing. But that’s what I am doing:
On the user I set a field enrolled: true as soon as the registration process is done. For email and password this is straight forward. When registering with OAuth after successful registration the user is immediately logged in. In the UI you can check for the presence of the enrolled field and if not present redirect to the page to finish registration. I have a publication which publishes the necessary field so you can reliably detect whether the user is actually enrolled or not:

Meteor.publish(null, function () {
  if (this.userId) {
    return Meteor.users.find(this.userId, {
      fields: { enrolled: 1 },
    })
  }

  this.ready()
})

On registration completion just update your user’s document and nothing should be deleted:

Meteor.users.update({ _id: this.userId }, { 
  $set: {
    termsAcceptedAt: new Date(),
    enrolled: true,
    // whatever fields you need to set
  },
})

Hope that helps.

*) edit:
Just found why your data get’s removed:
Meteor.users.update({_id: Meteor.userId()},info.prof);
You just pass the info.prof object to the update method. This replaces the entire document with that object. And why are you doing the update twice? Merge those two updates and don’t forget to use $set, otherwise it replaces the whole doc.

1 Like

I was updating twice as I know the first update consisted of new fields and did not rewrite over any previous data. I separated them to like test out how to merge the password info in services as it was the only previous entry the was being rewritten over.
Below is what I was doing originally but still the info from services is being written over. At this point, I feel I just need to find the right function that will overlay on top of the previous info in services instead of being rewritten.

My current rendition is as follows:

Meteor Method Call:

'submit form': function(event,template){
        event.preventDefault();
        var nameVar = template.find('#name').value;
        var emailVar = template.find('#email').value;
        var passVar = template.find('#password').value;
        const info = {
            name: nameVar,
            email: emailVar,
            pass: passVar,
        };
        Meteor.call('updateUser', (info),
            (err) => {
                if (err) {
                    alert(err);
                }
            }
        );
    }

The update server method itself:

updateUser: (info) => {
        var pass = passHash(info.pass);
        Meteor.users.update({_id: Meteor.userId()},{
            $set: {
                services: {
                    password: {
                        bcrypt: pass,
                    },
                },
                emails : [{
                        address : info.email,
                        verified : false,
                    }],
                profile : {
                    name : info.name,
                },
            }
        });
    }

The problem now is in how the document is updated

//Document as soon as you register with the 3rd party
{
    "_id" : "X6cckavjhmkGgubrR",
    "createdAt" : ISODate("2018-06-18T01:41:08.112Z"),
    "services" : {
        "coinbase" : {
            COINBASE PROVIDED INFO
        },
        "resume" : {
            "loginTokens" : [ 
                {
                    Login Stuff
                }
            ]
        }
    },
    "profile" : {
        Coinbase Made Info
    }
}

//The same document after I use the update method after the 3rd party register

{
    "_id" : "X6cckavjhmkGgubrR",
    "createdAt" : ISODate("2018-06-18T01:41:08.112Z"),
    "services" : {
        "password" : {
                    bcrypt: Some hashed password info
                     }
    },
    "profile" : {
       Updated and reconfirmed info
    },
    "emails" : [ 
        {
            "address" : "fsdfds@efsfs",
            "verified" : false
        }
    ]
}

As you can see the Coinbase passed info is being rewritten over by the by the password info.

The update statement should look like:

Meteor.users.update({_id: Meteor.userId()},{
            $set: {
                'services.password': {
                    bcrypt: pass,
                },
                emails : [{
                        address : info.email,
                        verified : false,
                    }],
                profile : {
                    name : info.name,
                },
            }
        });

BUT: Don’t do such updates. Use existing Meteor functionality. Such as: