Account Linking with passportjs in 3 minutes

What is Account Linking
Account linking is a great way to let users connect their existing accounts (i.e. Google, Facebook, Twitter, GitHub, e.t.c.) to a site’s account. Let’s say we have several social accounts and we’d love to let users log in with a social account at that specific time or a time in the future, all we have to do is to link them together.
For this article, we’d only cover linking a local account to a GitHub account using passport.js.
How Passport.js Approached Authentication/Authorization with GitHub
This was stated on their README. It does the basic thing — findOrCreate a profile, nothing more.
This implementation is not good if you have account linking in mind because all it does is to create a new account if the githubId isn’t found and if it’s found, it authorizes them. But, let’s say we try to authenticate them with a different provider — let’s say Facebook, it means we’d have a create a new account, thereby having several accounts for one user.
How I Initially Approached Account Linking
What I tried doing was — to match users by their email addresses because I believe an email address is unique to a specific user.
An email to a user (one-to-one mapping) did seem to be the breakthrough as shown in the code snippet — If an email is found, add the GitHub provider to the user’s account and if not found, create a new account with GitHub. Subsequent requests made to authorize the user with GitHub provider or any other providers lead to not having duplicate accounts.
I opened a Facebook account a long time ago with a Yahoo(Mail). Few years after creating the account, I heard about how great Google(Mail) is — and decided to create an account with them. Few months after having a GMAIL account, I registered on GitHub and decided to use the new GMAIL account since it’s pretty cool.
Our implementation did solve a problem and it thus, seem linked. But It’s not entirely accepted simply because users may use different email addresses in the various existing accounts like I did.
A Better Approach To Account Linking
I stated reasons above why we won’t be using that, so what are we going to use? Let’s see, in the code snippet below.
Oops! It’s pretty longer than the previous implementation, and pretty much like the initial passport’s approach to authentication. So, what’s new:
- passReqToCallback: This passes the request object to GitHubStrategy. That was fairly easy to guess. With the request object we’d be able to know if the user is authenticated. If the user is then authenticated, it means the user need to be authorized i.e. granted access to use GitHub next time.
- nextRoute: This was something I came up with to throw the user to a specific route after authorization. cb() takes an optional third parameter info, which can take anything you pass to it. Here, we’re passing the next route.
Let’s now see how we can make use of this nextRoute feature in the code snippet below leveraging passport’s custom callbacks.
Instead of making use of the regular callbacks, I decided to create a pretty cool one. This one takes info as the third argument, and it uses ES6 destructuring to take just the next route off it.
How it works is fairly simple — If we have a next route (throw the user to the next route), which means we’re authenticated already and only need add the provider details to the user account. If we don’t have a next route (throw the user to the root route) after being authenticated and save the provider details.
You may want to customize all of these to soothe your needs, feel free to do that. And, you might also want to check out a full implementation I made few weeks ago.