OAuth lets users log in to your app using an existing account from a provider like Google or GitHub, without ever sharing that provider's password with you. This lesson covers the OAuth 2.0 flow and implements it in Express with Passport.
How the OAuth 2.0 Authorization Code Flow Works
Your app redirects the user to the provider (Google), the user logs in and approves access there, and the provider redirects back to your app with an authorization code. Your server exchanges that code for an access token and user profile information, entirely server-to-server, your app never sees the user's Google password.
Passport.js abstracts almost all of this exchange behind a strategy package, passport-google-oauth20 for Google, passport-github2 for GitHub, so implementing it in Express requires configuration far more than manual protocol handling.
The strategy's callback receives the provider's profile once Google confirms the user's identity, your job is just to find or create a matching local user record.
The first route starts the flow, redirecting the user to Google's consent screen.
scope specifies which pieces of profile data you're requesting access to.
The callback route is where Google redirects back to after the user approves (or denies) access.
failureRedirect sends the user somewhere sensible if they deny access or something goes wrong.
OAuth Cheatsheet
Key terms and steps in a typical OAuth 2.0 login flow.
Term
Meaning
Authorization code
Short-lived code the provider redirects back with
Access token
Token exchanged for the code, used to fetch profile data
Client ID / Secret
Credentials identifying your app to the provider
Redirect URI / callback URL
Where the provider sends the user back to
Scope
Which pieces of data your app is requesting access to
Finding or Creating a Local User
Even though Google verified the user's identity, you still typically maintain your own local user record, linking it to the provider's unique profile ID so returning users are recognized on future logins.
async function findOrCreateUser(profile) {
let user = await User.findOne({ googleId: profile.id });
if (!user) {
user = await User.create({
googleId: profile.id,
email: profile.emails[0].value,
name: profile.displayName,
});
}
return user;
}
Environment Configuration for OAuth
OAuth requires registering your app with the provider's developer console to obtain a client ID and secret, and configuring the exact redirect URI your app will use, which must match exactly (protocol, domain, and path) between your code and the provider's dashboard.
Mismatching the redirect URI configured in the provider's dashboard versus the one used in code.
Committing OAuth client secrets directly into source control instead of environment variables.
Not linking the provider's unique profile ID to a local user, causing duplicate accounts on repeat logins.
Requesting broader scope permissions than the app actually needs.
Key Takeaways
OAuth 2.0 lets users authenticate via a trusted third party without sharing that provider's password with you.
The authorization code flow exchanges a short-lived code for an access token and profile data.
Passport strategy packages abstract almost all of the OAuth protocol details.
Always link a provider's unique ID to a local user record to correctly recognize returning users.
Pro Tip
Double, then triple, check that the callback URL registered in the provider's developer console exactly matches the one your app uses, mismatched redirect URIs are the single most common OAuth setup failure.
You now understand OAuth-based login. Next, learn how Express applications connect to databases in the Databases lesson.