Great question. The answer is far more complicated than it needs to be. Let’s think about this without PassportJS.
So, what we’re dealing with when we “log in” through a site like Google or Github is a protocol called OAuth. The gist of it is that users can authorize your app to do things with their account. Here are some of the steps (leaving out details for brevity):
From your webapp, the user clicks “Login via Umbrella Corp”.
Your server redirects the user’s client (browser, in our case) to
https://umbrellacorp.com/login?redirect=https%3A%2F%2Fyourwebapp.com%2Fredirect. The redirect query parameter is the route on your server that will handle user details, and the UmbrellaCorp server needs it. The request header also has your app details, most often a secret key and client ID, which identify the application that the redirect has come from. If the user hasn’t already, they’re prompted to login to their UmbrellaCorp account. Note that because we’re on the UmbrellaCorp site, your server never sees the user’s login details. After login, the user usually has to authorize your application to work on their behalf, and they will see a list of the permissions you specified in the application’s scope when you registered your app with UmbrellaCorp. The user clicks “Authorize”, and UmbrellaCorp’s server then redirects the user’s client again, this time to the address specified in the redirect query parameter above.
The user is back on your server,
https://yourwebapp.com, and the redirect has hit the route
redirect. The full address is
https://yourwebapp.com/redirect. UmbrellaCorp includes some tokens with the redirect. These are the keys you use to act on behalf of the user. So, maybe your app can be used to track the user’s prescriptions, set doctor’s appointments, or notify UmbrellaCorp of any unfortunate “side effects” that their drugs may or may not have on the user. UmbrellaCorp has an API for doing all of this, and each request that accesses user data will have to include the “keys” you got from the company’s server. An example of some API documentation would be:
curl -H 'Authorization: OAuth oauth_nonce="182973641298746", oauth_timestamp="1487111226", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="umbrella-987jkjla8j", oauth_token="076' -H "Accept: application/json" https://api.umbrellacorp.com/1234/prescriptions
Your server is responsible for almost everything in that
Authorization header (
oauth_version are constants, so you don’t have to create them). The consumer key and oauth token are what you’ve received from UmbrellaCorp, and your server will have to add those to the header with each request. You will need to store these in a database on your server in order to access them. Your server will also need to keep track of when your user is logged in to your app, which means sending a cookie to the browser for storage, or a JWT with every request and response. The user would click a button on your app which sends a request to your server, and this request has some identifying information in the header. Your server uses this to look up the stored UmbrellaCorp credentials.
Even if you don’t plan on using UmbrellaCorp’s API, you can use store the OAuth credentials as a way to say the user is “logged in”. When your user wants to log out, you remove those credentials, and when the log back in, we can just start over from step 1 above. If you don’t do this, then you need to handle user authentication on your own server, which is totally possible (even less painful than OAuth, I think), but much less secure.
Keep in mind that this example is simplified. OAuth 1.0 has different requirements from OAuth 2.0. For instance, OAuth 2.0 tokens will expire, but you get an additional “refresh token” that can be used to get a new authorization token without having to log in all over again. As you can imagine, this complicates things considerably.
You may be asking yourself some questions right now. “Does this really have to be so complicated?”, “Is it still happy hour at the pub?”, and “Why? WHY?! WHYYYYYYYYY?!?!” are all common questions that people have when first implementing OAuth. This is where PassportJS comes in.
PassportJS has a collection of “strategies”, each of which takes care of these blood-curdling details by abstracting it all into a simple interface for you, the Node developer. You set up something like express-session, which will take care of using cookies in the browser to persist logins, and then Passport will present the user’s data in a way that makes it very easy to save to a database. Passport automates many of the smaller details so you don’t have to write out headers.
For what it’s worth, I’ve found Grant easier to use. It’s an OAuth library like Passport, but you can plug in a database and session object, and then Grant will handle cookies, finding and storing user data, and almost everything else about OAuth. I used it in a recent personal project, which you can find here and the source code here.
tl;dr: PassportJS lets you create super complicated OAuth requests, which will only authenticate the user with the OAuth server, using a simple interface, but it’s up to you to store and handle the data that it gathers.