Under the Hood
The story began when my friend asked me about integrating Passport.js and JSON Web Token (JWT)) together. He wanted to create an API or endpoint providing Google authentication using Passport.js with Express and JWT. So I thought, why not write a story to help many developers in the market so that you don’t have to read StackOverflow or the documentation?
Getting Started
Before we start, remember that Google authentication or Facebook or Github requires a client ID and client secret which you can grab from their corresponding developer console or dashboard. But overall, all third-party OAuth works on the same principle of client id and client secret. In just four steps, we will create a Google authentication API with Passport.js and JWT.
Overall Flow
This will be the overall flow —
- Initialize the passport by adding configuration to the strategy instance.
- Create a route /v1/auth/google, which will begin the google authentication and open the google email selection modal for users. (Basically redirect to the google login page)
- Add passport google authentication as a middleware for /v1/auth/google the route; being middleware, we will call the passport authentication method and provide our client id and the secret to the middleware function.
- Once the passport _verify callback is called successfully, it will return the profile containing the user email and username. Using the user profile details produced from passport middleware, create a JWT token and store the user details in the database with a ticket if you want.
- Lastly, handle the callback URL you mentioned in the google developer console in the custom server. This callback URL will handle the redirection or passing token to the client in response.
Initializing Passport.js
Initialising is quite simple as it only needs 3/4 steps.
- Add required Passport.js npm packages such as passport & passport-google-oauth20
- Pass the auth strategy and callback to the passport use method.
- Auth strategy added in the params need clientId,clientSecret & callbackURL
- Serialize and Deserialize the user send by passport using its corresponding methods.
- Lastly, add this confirmed passport instance and initialise them using express middleware methods.
Inside the middlewares/passport directory, add the following code.
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
callbackURL: 'http://localhost:3001/auth/google/callback',
clientID: '499314077621-n7klfql9rfd8ho7kl26bu6p1vf9fgg5h.apps.googleusercontent.com',
clientSecret: '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',
},
(accessToken, refreshToken, profile, done) => {
console.log(profile, 'profile')
done(null, user);
}),
);
passport.serializeUser((user, done) => {
if(user) return done(null, user)
else return done(null, false)
}),
passport.deserializeUser((id, done) => {
if(user) return done(null, user)
else return done(null, false)
}),
module.exports = passport;
Lastly, initialise the exported passport using the express middleware method by adding the following code.
const passport = require('./middlewares/passportMiddleware');
server.use(passport.initialize());
Let me explain what we're doing here —
- We pass the auth strategy to the passport use method, and since we are using google authentication,
clientId
andclientSecret
and callbackURL are required. - Then, we have just defined the callback function invoked when everything is successful from the passport end.
- Lastly, we have just added serialised and deserialised method
- Then, inside the
server.js
file, we initialise that exported passport method using the express use method. Note — Please do not remove serialise and deserialise methods as we need them to read the logged-in user data.
Creating a route for Google login
It's a primary GET route. We don’t have to send anything from the client side. The agenda of this route will be to invoke passport middleware.
const passport = require('passport');
router.get('/v1/auth/google', passport.authenticate('google', { session: false, scope: ['profile', 'email'] }));
We have also provided the scope and authentication strategy for the passport authentication method.
Saving User Profile and Creating JWT token
Here comes the climax of the story; once the passport middleware gets invoked and we get the user profile details, we have to deal with JSON web token and our database system. It is totally up to your choice on what to do with tickets, some developers prefer to store tokens in the database, and some do not. I always send the key to the cookie once the user logs in successfully.
Inside Passport.js, when the callback for the passport use method is called, it will return the user profile we have already consoled. This is where we will create our JWT token, and if you want, you can store the user details in the database.
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const jwt = require('jsonwebtoken');
passport.use(new GoogleStrategy({
callbackURL: 'http://localhost:3001/auth/google/callback',
clientID: '499314077621-n7klfql9rfd8ho7kl26bu6p1vf9fgg5h.apps.googleusercontent.com',
clientSecret: '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',
},
(accessToken, refreshToken, profile, done) => {
// Check if user with same email or id exists in DB if not create one and save in DB
const token = jwt.sign({ email: profile.emails }, process.env.JWTSecretKey,{ expiresIn:'14d'});
const user = {
email: profile.emails,
username: profile.username,
id: profile.id,
profileUrl: profile.profileUrl,
token
};
// Now token and user are ready store them in DB
done(null, user);
}),
);
passport.serializeUser((user, done) => {
if(user) return done(null, user)
else return done(null, false)
}),
passport.deserializeUser((id, done) => {
if(user) return done(null, user)
else return done(null, false)
}),
module.exports = passport;
Handling Callback URL
The last part is to handle the callback redirect URL. If you remember, we have added a callback URL in the google developer console, but that URL doesn’t exist in our custom server. In this current case, once the token is created, the user has logged in, and the user will be redirected to the redirect callback URL. Now it's our job as developers to define the actual method or route to which the logged-in user should be thrown.
const passport = require('passport');
router.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/'}),(req, res) => {
res.cookie('authToken', req.user.token);
res.redirect('/');
});
I am sending the token in the cookie to the user and redirecting him when they have logged in successfully. This callback URL will only get called when everything went as planned, and the user has logged in successfully using google authentication.
On the client side, grab the token from the cookie and verify it using another API to confirm its authenticity and confirm that the user is logged in successfully.
Saving tokens in cookies has its pros and makes it easy to handle user sessions across multiple tabs and devices.
Conclusion
I comprise the story in 4 steps, but it took some time for me to grasp the idea initially, especially if you are new to this Node and Passport world. That is why I tried to finish the entire story in 4 steps so that you can remember those four steps simultaneously. Until next time, have a good day, people.
Keep developing
Shrey
iHateReading
Top comments (2)
Super ,et j'ai besoin de votre aide pour finaliser un projet sur Nextjs svp
Sorry but didn't understand it all