Duplicate email addresses in the database

I noticed that in our users collection we have several users with the same email address. This is causing login issues. Our app is a fairly standard Meteor app with the google accounts package. We have over 20k users and there are just two dups in the database.

This was discovered using the following mongo query:

res = db.getCollection('users').mapReduce(function () {
    emit(this.services.google.email, 1);
}, function (k, vals) {
    return Array.sum(vals);
}, { out:{inline : 1}});
res.results.forEach(function(account){
  if (account.value > 1) print (account)
});

Does anyone else have this issue? Is there a reason the Meteor accounts package doesn’t put a unique index on services.*.email?

The meteor docs only guarantees uniqueness of emails in the emails field of a user. I would assume that the reason they don’t enforce uniqueness from an external service is because they have no way of knowing that the external service in fact also guarantees uniqueness.

Which is in fact true for Google. For example the following email addresses are all the same address according to Google. And no this isn’t a bug its a Gmail feature.

But those email addresses you listed are not exact duplicates - like what I’m seeing. I’m talking about where the string in the “services.google.email” key is exactly the same for two entries in the database. See my above mapReduce query.

So I have done some sleuthing and here is the deal. Meteor is actually using a deprecated Oauth API for Google, and since deprecating it Google has removed the docs on it. But from poking through the Meteor source code it appears that Google used to return a single email address for an account and this is the email that is being used. The new Oauth API instead returns a list of emails. The only value that is guaranteed to be unique from an Oauth authorization flow is the access token.

Either way no matter what you are doing you should make no assumptions about profile data, including email, being sent from a third party. There may be duplicates or malformed data or any other of strange issues.

You still might be asking, but how did I get 2 emails from google, and I have worked out the reason after some serious digging through Meteor source code and Google Oauth APIs. If a user revokes access to a service through Google, it will invalidate the token stored by Meteor, but Meteor has no way of noticing this. If that same user tries to login again it will instead create a new account (which is expected behaviour) but the old account will still be stored.

Hopefully this answers your question, thanks for asking it, I learned a bunch about how Meteors external login services work in the process :smile:

1 Like

Awesome, thanks! Should this be filed as a Meteor bug then?

No its expected behaviour, if a person revokes access to a service it is equivalent to deleting their account. When they ask to join again they should get a fresh account.

Imagine some Oauth service XXX that provides profile info including an email address, then the person using that service deletes their account or revokes their token and changes their email address. If someone else creates a new account using the old deleted or changed email and you use this to test for uniqueness in your app you have now given a stranger access to the original persons account info.

With Oauth the only safe assumption you can make is if a user logs in and the token you get from the 3rd party service is the same as the token you have stored then it is the same person. You cannot use profile info for authentication, you cannot delete the old account since Oauth2 does not have revoke functionality, just because a token is invalid now doesn’t mean it always will be.