API Authentication with JWT and Passport
In this tutorial, I am going to show you how authenticate your API using JWT and Passport. JWT is an open standard that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.
Prerequisites
Setting up the Project
First you need to create a directory for your project. If you are going to use terminal to create directory, run below command on your termianl.
1
mkdir jwt-and-passport-auth
1 | mkdir jwt-and-passport-auth |
Then navigate to your project directory.>
1 | cd jwt-and-passport-auth |
Then we need to initialize a new package.json:
1 | npm init -y |
Next, we need to install to required dependencies:
1 | npm install --save bcrypt body-parser express jsonwebtoken mongoose passport passport-jwt passport-local |
We need,bcrypt
for hashing passwordjsonwebtoken
for signing tokenspassport-local
for implementing local strategypassport-jwt
for retrieving and verifying JWTs
At this point, we have been initialized and all the dependencies have been installed. Usually we start a project with creating our entry file such like app.js but in here, I start with creatind Database Schema.
Setting up the Database
Create a directory call model
in your project's root directory.
1
npm init -y
1 | npm init -y |
Inside the model
directory create a file namedmodel.js
and place below code,
1 | const mongoose = require('mongoose'); |
When you store your passwords, you should avoid storing passwords in plain text.
To avoid this, we will use a package called bcrypt
to hash user passwords and store them safely. Add the library and the following lines of code:
1 | // ... |
UserScheme.pre()
function will get the plain text password, hash it, and store it before user information is saved in the database.this
refers to the current document about to be saved.
Then we also need to make sure that the user trying to log in has the correct credentials. To validate is add below code after the UserScheme.pre()
function:
1 | // ... |
bcrypt
hashes the password sent by the user for login and then checks if the hashed password stored in the database matches the one sent by the user. It will return true if there is a match. Otherwise, it will return false.
Setting up Registration and Login Middleware
In here, we used Passport authentication middleware to authenticate requests.
It allows developers to use different strategies for authenticating users, such as using a local database or connecting to social networks through APIs like login with Twitter, LinkedIn, Facebook, GitHub and many more.
In here, we use the local strategy (email and password).
You will use the passport-local
strategy to create middleware that will handle user registration and login. This will then be plugged into certain routes and be used for authentication.
To implement this authentication, we create and directory called auth
and create new file called auth.js
within it. Then you should start it by requiring passport
,passport-local
, and the UserModel
that was created in the previous step:
1 | const passport = require('passport'); |
First, add a Passport middleware to handle user registration:
1 | // ... |
Main functionality of this code is save the information provided by the user to the database, and then sends the user information to the next middleware if successful.
Next, add a Passport middleware to handle user login:
1 | // ... |
This code will use to finds one user associated with the email provided.
Creating the Signup Endpoint
To provide routing, we use popular Node.js framework Express. If you want to know more about Node.js frameworks, you can read my article about Node.js frameworks from here.
To provide routing functionality to our application, we need to create a directory called routes
and create new file called routes.js
within it.
Then you should start by requiring express
and passport
:
1 | const express = require('express'); |
Then we need to handling the POST
request for signup
:
1 | // ... |
Creating the Login Endpoint and Signing the JWT
To proceed with Login, first we need to require jsonwebtoken:
1
2
3
4
5
6
const express = require('express');
const passport = require('passport');
const jwt = require('jsonwebtoken');
// ...
1 | const express = require('express'); |
Then we need to handling the POST
request for login
:
1 | // ... |
We should not store sensitive information such as the user’s password in the token.
We store the id
and email
in the payload of the JWT. You then sign the token with a secret or key (TOP_SECRET). Finally, you send back the token to the user.
We have login endpoint now . A successfully logged in user will generate a token. Now we need to Verifying the JWT.
Verifying the JWT
To verifying the JWT, put below code below the require and above Passport middleware of handle user registration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ...
const JWTstrategy = require('passport-jwt').Strategy;
const ExtractJWT = require('passport-jwt').ExtractJwt;
passport.use(
new JWTstrategy(
{
secretOrKey: 'TOP_SECRET',
jwtFromRequest: ExtractJWT.fromUrlQueryParameter('secret_token')
},
async (token, done) => {
try {
return done(null, token.user);
} catch (error) {
done(error);
}
}
)
);
1 | // ... |
This code uses passport-jwt
to extract the JWT from the query parameter. It then verifies that this token has been signed with the secret or key set during logging in (TOP_SECRET
). If the token is valid, the user details are passed to the next middleware.
Creating Secure Routes
Now, we need to create secure routes that only users with verified tokens can access.
Create a new filesecure-routes.js
in routes
directory and add the following lines of code:
1 | const express = require('express'); |
This code handles GET
request for profile
. It returns a “You made it to the secure route” message also also returns information about the user and token.
Create app.js
Now create file called app.js
which we are going to implement our database connection and etc. Then paste below code in it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const express = require('express');
const mongoose = require('mongoose');
const passport = require('passport');
const bodyParser = require('body-parser');
const UserModel = require('./model/model');
mongoose.connect('mongodb://127.0.0.1:27017/passport-jwt', { useMongoClient: true });
or
mongoose.connect("mongodb+srv://<user>:<password>@learning.eauwn.mongodb.net/<database>?retryWrites=true&w=majority", {
useNewUrlParser: true,
useUnifiedTopology: true,
});
mongoose.connection.on('error', error => console.log(error) );
mongoose.Promise = global.Promise;
require('./auth/auth');
const routes = require('./routes/routes');
const secureRoute = require('./routes/secure-routes');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use('/', routes);
// Plug in the JWT strategy as a middleware so only verified users can access this route.
app.use('/user', passport.authenticate('jwt', { session: false }), secureRoute);
// Handle errors.
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.json({ error: err });
});
app.listen(3000, () => {
console.log('Server started.')
});
1 | const express = require('express'); |
Run Application
To run your application, use below command,
1
2
3
4
5
6
nodemon app.js
or
node app.js
1 | nodemon app.js |
Testing with Postman
* Signup
Method: POST
URL:http://localhost:3000/signup
Body
x-www-form-urlencoded
Key | Value |
---|---|
example@example.com | |
password | password |
- Login
Method: POST
URL:http://localhost:3000/login
Body
x-www-form-urlencoded
Key | Value |
---|---|
example@example.com | |
password | password |
- Profile
Method: GET
URL:http://localhost:3000/user/profile
Params
Key | Value |
---|---|
secret_token | token |