Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge identites by email overrides the original user_id #71

Closed
fedescarpa opened this issue Jun 19, 2016 · 36 comments
Closed

Merge identites by email overrides the original user_id #71

fedescarpa opened this issue Jun 19, 2016 · 36 comments

Comments

@fedescarpa
Copy link

fedescarpa commented Jun 19, 2016

I am using the rule to merge identites by email. However, when merge occurs, the user changes its user_id to the last recently identity added.

Such behavior causes a lot of identity issues - for example, when you try to get the user in a database using the user_id as search key.

Instead of overriding the original user_id, this rule should just add the new identity to the original one, and keep it's user_id

@bragma
Copy link

bragma commented Nov 23, 2016

Hi,
is this issue solved in the latest version of the rule? The problem seems to be addressed in this PR a9e3944 but the change has been overwritten by later commits.
I'm wondering if the problem is still there.

@AmaanC AmaanC self-assigned this Dec 30, 2016
@AmaanC
Copy link
Contributor

AmaanC commented Jan 6, 2017

Update

This has been fixed properly in this PR (with a corresponding update to our cloud deployments). For private service deployments, update / contact your Auth0 Customer Service Manager.

Workarounds

The way that Auth0's authentication flow currently works, there would be 2 options:

1. You try to merge the new user into an old user in the Rule.

The authentication flow for the new user just fails outright, because the Rule "deleted" their user profile and merged it into another. You would see an error like, The code was generated for a user who doesn't exist anymore.

This is obviously unacceptable, as it would mean asking the new user to sign in yet again. @bragma This is what would happen in the PR you mentioned, and that's why it was overwritten.

2. Instead of merging the profiles in the Rule, you simply call a Webtask (or your own server) to merge the user profiles.

This way, the Rule makes an external call and the authentication flow succeeds, albeit with your client application receiving the non-merged user profile - i.e. the new user profile.

Then the Webtask can merge the new user into the old one - giving you the functionality you want of preserving the original profile's user_id. Except that in this case, your client application would also need to realize that the profile it has is outdated. In that case, you would need to use the user-profile as returned by the Webtask using API v2 instead of the user-profile returned by Auth0.

If there is still interest in this, we can look into figuring out more specifics.

If not, I'll file this as an enhancement request so we can consider a better way to merge user-profiles during the authentication flow instead of needing them to happen after.

Let me know!

@AmaanC
Copy link
Contributor

AmaanC commented Feb 6, 2017

Closing due to inactivity. Ping us if you're still interested

@AmaanC AmaanC closed this as completed Feb 6, 2017
@gdestuynder
Copy link

FYI we're running in the same issues.
In particular, in your example with a separate webtask this would happen after the user already logged in with both accounts right?

I think the main benefit of user-linking is that it merges attributes from different accounts into one, and let the user login with any account. Say, the user logins with GitHub but has an AD attribute that the applications want to lookup, if the linking does not happen during the login flow - but through a separate web task, that would mean that at first the user does not have all attributes.
The user only would get all attributes by login back in later once the webtask has run. (let me know if I misunderstood this)

@AmaanC
Copy link
Contributor

AmaanC commented Feb 13, 2017

@gdestuynder Yep, that's right.

The user only would get all attributes by login back in later once the webtask has run. (let me know if I misunderstood this)

That would work, though it isn't 100% necessary. If your backend / Webtask has access to Management API v2, you could simply use that to get the updated user-profile (with both old and new identities) "immediately" (with a slight delay for the account linking to complete).

So the flow would be:

  • Login
  • Rule calls Webtask
  • Login completes
  • Webtask links the 2 accounts
  • Client has old user-profile. It can use Management API v2 itself or ask the Webtask to use it to get the user's profile after linking is complete

That way the user doesn't need to login a second time - your client would just need to realize when account linking is happening, and it would need to know to query API v2 manually instead of merely using the profile returned from /userinfo (or the id_token) (since the access_token for /userinfo would be invalid because the new user has been merged)

@gdestuynder
Copy link

My (and I think other's) problem is that his involve client code which is not always possible (specially if you have tens of clients or more, some being SaaS) and user experience would be inconsistent. (that's why we try to get it in the rules)

@AmaanC
Copy link
Contributor

AmaanC commented Feb 16, 2017

@gdestuynder The way account-linking works in Rules, the updated and linked user-profile isn't returned during the first authentication flow anyway, regardless of the issue at hand (i.e. linking the new user into an old user vs. linking an old user into the new user).
I'll follow-up internally to see if that's something we can consider changing.

Regardless, assuming you don't use our new API Authorization features you can currently do this using a Rule like this anyway:

function (user, context, callback) {
    // Call API v2 to fetch the original user that this user should be linked with
    var originalUser = ...;

    // Add the appropriate guards here, such as checking if the user's email is
    // verified, etc.

    var whitelistedKeys = ['adProp1', 'adProp2'];
    whitelistedKeys.forEach(function(key) {
        // Copy claims from original user to the newly logged in user
        // After the login, you can link the new user into the original user too
        user[key] = originalUser[key];
    });
    callback(null, user, context);
}

@mattdodge
Copy link

I'm in for a +1 on this also and would propose re-opening it since it doesn't seem inactive anymore.

One of the reasons I (and I assume others) pay for Auth0 is so that you guys manage the user database for me and I don't have to. I can rely on the user ID that comes back in tokens and API requests to know which user it is. The problem with this account linking setup is that once a new login method is introduced and a user uses it, the old user ID essentially disappears. The only way to know about it is for me to keep a database or list of users and user IDs on my end which I don't want to do.

However, I do appreciate the technical complexity that is involved. The user starts the login with one user ID and after the rule hits gets a different one. Certainly bizarre. I just think this is a valid feature/enhancement request and unless it's a wontfix it would be nice to track progress somewhere.

@AmaanC
Copy link
Contributor

AmaanC commented Feb 22, 2017

@mattdodge Yep, certainly makes sense. I'll leave it open until we have further updates regarding a status. Do note that it may be a while, however!

@AmaanC AmaanC reopened this Feb 22, 2017
@gdestuynder
Copy link

There's a lot of caveats to be taken care of with the feature though, when you start linking accounts - for example deciding who's the primary account (say you login with github then google, but someone else login with google then github).
I tried something similar to the proposal in #71 (comment) but that did not work - might be my mistake, or that we're running one of the private auth0 instances (not the public cloud).

In the mean time, another similar solution is to enrich the profile of users with the data you want regardless of the actual user id (for ex adding a "groups" attribute). I could do this by updating the user metadata, and reflecting the user metadata into the user profile in a rule. It works like this: mozilla-iam/mozilla-iam#7 (comment)

@cwhittl
Copy link

cwhittl commented Mar 10, 2017

I came to this with the same problem and I used https://github.com/auth0/rules/blob/a9e3944c45cdde06aa02fa467e21e0ca0168a632/rules/link-users-by-email.md and it seems to work for my situation.

@AmaanC
Copy link
Contributor

AmaanC commented Apr 19, 2017

@cwhittl Care to elaborate how you're using that Rule? I don't see a way in which that Rule will work for you - the first login will fail when the primary user ID (used for linking) is not the same as that of the user logging in.
Subsequent login attempts will work, of course, as I outlined in option 1 above.

Is there a scenario I'm missing that you're using?

@hmitsch
Copy link

hmitsch commented May 4, 2017

@AmaanC, is there an expected delivery date for this capability?

@AmaanC
Copy link
Contributor

AmaanC commented May 4, 2017

@hmitsch It's on our backlog, but we don't have an ETA at the moment. Sorry!

@hmitsch
Copy link

hmitsch commented May 4, 2017 via email

@AmaanC
Copy link
Contributor

AmaanC commented May 8, 2017

Tentatively in 2017 - we may need to adjust based on priorities, though.

@hmitsch
Copy link

hmitsch commented May 8, 2017 via email

@mikebridge
Copy link

I'm trying to create a password-based account via the Management API v2, then invite a user to log in to that account. But I just made the unpleasant discovery that if an invited user selects another authentication method when logging into this account, the merge rule wipes out the original userid, and on top of that it deletes all the user's Authorization Extension roles and groups.

@AmaanC's suggestion to work around this by "simply" using a webtask sounds like an exceedingly complex workaround to me. Shouldn't attaching a new auth method to an account keep the old account details the same? Isn't that what anyone would expect? Maybe I'm missing something, but I can't imagine a situation where the current merge-rule functionality would actually be useful.

@bryanculbertson
Copy link

My company only uses a single login option (email-password). It just isn't practical to use multiple login options until Auth0 figures out how to allow merging of accounts while keeping the user id stable.

@mikebridge
Copy link

After researching this some more, I don't see that creating a "temporary" account and merging it behind the scenes via the API during the first login is a viable option---it would mean I would have to keep track of a temporary user id for a user's first session and also somehow deal with the missing metadata from the original JWT like roles and groups. I suppose I could copy all the original info into the user's JWT token somehow as custom data but yuck.

And it sounds like rules don't allow you to switch the id of the authenticated user during the login flow, so a rule-based merge is currently impossible. Frankly, I'd remove this page from the site since I'm guessing that no developer would expect a user's ID to change.

It sounds like the least complex user experience with auth0 is to merge the account manually via the API when the user first logs in with a different auth method, and then tell the user to log out and log back in again. :(

@mikebridge
Copy link

@bryanculbertson Managing multiple authentication schemes is the main reason why I switched to Auth0 from another solution. I saw that documentation before switching and assumed the functionality worked. Now it looks like the feature is full of bugs and they have no plans to fix it.

@bryanculbertson
Copy link

@mikebridge It is also the main reason I choose auth0. I wish their documentation had been more forthright about the caveats of using it.

Considering the response to the bug, I have resigned myself that I am only ever going to use Auth0 for email- password login, and when I want social sign in I will switch to my own auth.

@AmaanC
Copy link
Contributor

AmaanC commented Jul 29, 2017

Thanks for the feedback, guys! I'll look into making the caveat more explicit and I definitely agree regarding the behavior one would expect from the feature.

We're definitely planning to fix it - we just don't provide ETAs as a matter of policy. It's also much higher on our priority list than before, if that's any comfort. Sorry about the inconvenience!

@Mte90
Copy link

Mte90 commented Aug 11, 2017

Any updates for this feature?

@AmaanC
Copy link
Contributor

AmaanC commented Aug 14, 2017

@Mte90 Nothing to share at the moment, but I'll post here as soon as there is something.

@Johnnyrook777
Copy link

Bump

@AmaanC
Copy link
Contributor

AmaanC commented Sep 7, 2017

We're still working on docs for this issue, but the feature is ready and has been rolled out. To use it, set context.primaryUser = primaryUserId (where primaryUserId === 'auth0|foo123' for example).

Sample Rule:

function(user, context, callback) {
  var request = require('request@2.56.0');
  // Check if email is verified, we shouldn't automatically
  // merge accounts if this is not the case.
  if (!user.email_verified) {
    return callback(null, user, context);
  }
  var userApiUrl = auth0.baseUrl + '/users';

  request({
      url: userApiUrl,
      headers: {
        Authorization: 'Bearer ' + auth0.accessToken
      },
      qs: {
        search_engine: 'v2',
        q: 'email.raw:"' + user.email + '" AND email_verified: "true" -user_id:"' + user.user_id + '"',
      }
    },
    function(err, response, body) {
      if (err) return callback(err);
      if (response.statusCode !== 200) return callback(new Error(body));

      var data = JSON.parse(body);
      if (data.length > 1) {
        return callback(new Error('[!] Rule: Multiple user profiles already exist - cannot select base profile to link with'));
      }
      if (data.length === 0) {
        console.log('[-] Skipping link rule');
        return callback(null, user, context);
      }

      var originalUser = data[0];
      var aryTmp = user.user_id.split('|');
      var provider = aryTmp[0];
      var newUserId = aryTmp[1];
      request.post({
        url: userApiUrl + '/' + originalUser.user_id + '/identities',
        headers: {
          Authorization: 'Bearer ' + auth0.accessToken
        },
        json: {
          provider: provider,
          user_id: newUserId
        }
      }, function(err, response, body) {
        if (response.statusCode >= 400) {
          return callback(new Error('Error linking account: ' + response.statusMessage));
        }
        context.primaryUser = originalUser.user_id;
        callback(null, user, context);
      });
    });
}

Note: If you see the Unable to construct sso user. error, odds are that it's caused due to setting context.primaryUser to a non-existent user_id.

Could you guys thumbs-up to let me know if this fixes the original issue you had?

I'll close it once docs are in place and the Rule templates have been updated.

@mpaktiti
Copy link
Contributor

Reference documentation and rule templates updated:
auth0/docs#5021
#122

@jonathanfishbein1
Copy link

jonathanfishbein1 commented Sep 27, 2017

Which rule template should we use to get this functionality? When can we expect the updated template to be in the Auth0 web interface?

@thameera
Copy link
Contributor

@jonathanfishbein1 You can use any of the following rules:

We will update these in the dashboard's Templates section soon, but until then please copy/paste from the above links.

@jonathanfishbein1
Copy link

@thameera Thank you for the response. I have copied and pasted the code from link-users-by-email. However, the dashboard is showing me errors

`The script has errors. Do you want to save it anyway?

Line 34: 'user_id' is not defined.
Line 35: 'user_id' is not defined.
Line 36: 'user_id' is not defined.`

@thameera
Copy link
Contributor

@mpaktiti I think it should be originalUser.user_id there? ^

@thameera
Copy link
Contributor

Hi @jonathanfishbein1, the issue is fixed with #124.

@mikebridge
Copy link

@AmaanC This is working for me, thanks!

@triadzack
Copy link

Several years later I am running into a case where I have users who can sign in with either a magic link or a password and it creates them as two users. All the documentations and rules mentioned in this ticket that would seemingly solve this problem have been removed. Is there a better way of doing it these days?

@thameera
Copy link
Contributor

@triadzack You can take a look at the Account Link extension - but please continue the discussion in Auth0 Community or via Support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests