What is PassportJS And How To Integrate It?
Tags: node, javascript, passportjs, webdev
Published: 2019-12-23 08:49:30

<p>By <a href="http://www.passportjs.org/">the official definition: </a></p>
<p><em>"Passport is authentication middleware for Node.js. </em></p>
<p><em>Extremely flexible and modular, Passport can be unobtrusively dropped in to any Express-based web application. </em></p>
<p><em>A comprehensive set of strategies support authentication using a username and password, Facebook, Twitter, and more."</em></p>
<p>So basically PassportJS manages session user for us and gives us a lot of strategies to use when we want to integrate login/register options in our application.</p>
<p>Let's see how can we integrate it.</p>
<h2><b>Integrating PassportJS</b></h2>
<p>Let’s start with a base for our app:</p>
```javascript
const express = require('express')

const app = express()
const port = 3000

app.listen(port, () => console.log(`App listening on port ${port}!`))
```
<p>Now we can start configuring passport. </p>
<p>We will need to configure session middlewares as well as cookie-parser and body-parser:</p>
```javascript
const cookieParser = require('cookie-parser')
const bodyParser = require('body-parser')
const expressSession = require('express-session')
const passport = require('passport')

...

app.use(cookieParser())			 // 1
app.use(bodyParser.json())		 // 2
app.use(bodyParser.urlencoded({
  extended: true,
}))						         // 3
app.use(expressSession({
  secret: '5om35ecr37',
  resave: false,
  saveUninitialized: false,
}))						         // 4
app.use(passport.initialize())	 // 5
app.use(passport.session())		 // 6
```
<p>Let’s have a look and explain what each line does:</p>
<ol><li>Middleware which parses Cookie header values and adds cookies property to request object containing all the cookies keyed by cookie names.</li><li>Parses request body as json data and adds it to body property in request object.</li><li>Adds support for parsing request body in URL format.</li><li>MIddleware for generating session cookies and matching them with data stored server side i.e logged-in user.</li><li>Initializes passport core which runs strategies and manages them.</li><li>Add support for managing user data in the session.</li></ol>
<h2><b>Adding local strategy</b></h2>
<p>Now that we have all middlewares configured we can add a strategy to our app. </p>
<p>In this case, it will be a local strategy.</p>
<p>First, we need to install a selected strategy:</p>
```javascript
npm install passport-local
```
<p>Or:</p>
```javascript
yarn add passport-local
```
<p>Now we can configure it.</p>
<p>To simplify this tutorial I will return static user object in our callback:</p>
```javascript
const userStore = {
  findByUsername: () => ({
    id: 1,
    name: 'Test User',
    username: 'testuser',
    validatePassword: () => true
  })
}

passport.use(new LocalStrategy(
  (username, password, done) => {
    const user = userStore.findByUsername(username)

    // User does not exist in our database or password was invalid
    if (!user || !user.validatePassword(password)) {
      return done(null, false)
    }
    // If user was found and password is valid we can return user object
    // in callback
    return done(null, user)
  }
))
```
<p>And that’s it!</p>
<p>Our login form logic is ready.</p>
<h2><b>Session serializing and deserializing</b></h2>
<p>When we are using <em>passport </em>with session support we have to implement two callbacks which passport will use to serialize and deserialize the user.</p>
<p>Serialize callback is called when the<em> user </em>is saved to the <em>session</em>. </p>
<p>We will use <em>user id</em> which will allow us to find the <em>user </em>in the database later.</p>
```javascript
passport.serializeUser((user, done) => {
  done(null, user.id)
})
```
<p>Deserialize callback is used to find the <em>user in the </em>database based on data stored in cookies.</p>
<p>In our case data stored in cookies is <em>user id.</em></p>
```javascript
const userStore = {
  ...,
  findUserById: userId => ({
    id: 1,
    name: 'Test User',
    username: 'testuser',
    validatePassword: () => true
  })
}

passport.deserializeUser((userId, done) => {
  // Find user in database by id from session
  const user = userStore.findUserById(userId)


  // If user was not found throw an error
  if (!user) {
    done('unathorized')
    return
  }


  // If user was found return it via callback, this user object will be  
  // available in  request object as user property
  done(null, user)
})
```
<h2><b>Adding login form</b></h2>
<p>Now we can add login form to our app. </p>
<p>To do that let’s define simple HTML form.</p>
```html
<html>
<head>
  <title>Awesome login form</title>
</head>
<body>
  <form action="/" method="POST">
    <label for="username">
      Username:
    </label>
    <input name="username" id="username" />
    <label for="password">
      Password:
    </label>
    <input type="password" name="password" id="password" />
    <button type="submit">
      Login
    </button>
  </form>
</body>
</html>
```
<p>And return this HTML file on root URL of our app:</p>
```javascript
const path = require('path')

app.get('/', (request, response) => {
  response.sendFile(path.resolve('./login.html'))
})
```
<p>Now we have to define the path to handle POST requests from our form:</p>
```javascript
// This will print “Logged in!” text after successful login
app.post(
  '/',
  passport.authenticate('local'),
  (request, response) => {
    response.send('Logged in!')
  }
)
```
<p>Let’s go back to our root URL handler and return different <em>view </em>if the<em> user </em>is logged in. </p>
<p>First, we have to create HTML file for our logged-in view:</p>
```html
<html>
<head>
  <title>Awesome logged in view</title>
</head>
<body>
  <a href='/logout'>Logout</a>
</body>
</html>
```
<p>And change our root URL handlers like this:</p>
```javascript
// This will redirect user back to root url after logging in
app.post('/', passport.authenticate('local', { successRedirect: '/' }))

app.get('/', (request, response) => {
  const viewPath = request.isAuthenticated()
    ? './loggedIn.html'
    : './login.html'

  response.sendFile(path.resolve(viewPath))
})
```
<p>As you can see I have defined “/logout” URL in our logged in view, so we have to handle that URL as well. </p>
<p>Clicking on this link will log out our user and redirect him back to the login form.</p>
```javascript
app.get('/logout', (request, response) => {
  request.logOut()

  response.redirect('/')
})
```
<h2><b>Summary</b></h2>
<p>If you work with NodeJS, PasportJS is a great authentication tool, that you can easily customize for different strategies.</p>
<p>Let me know what you think! </p>