Introduction
Every developer at any point of his development process is faced with the inescapable, which is to implement an authentication feature for his application. This task, though has been made easier for us with the abundance of third party applications, dependencies and in-built authentication features in most frameworks, we easily forget to take a look at how the whole authentication process works and looks like. In this series of posts, we will go over the various authentication possibilities available for us in NodeJS. We will first start with HTTP Basic Auth, then move to use Cookies, Session, JWT and passport to implement the same authentication. Though all accomplish the same goal, one is much more secure than the other. While treating each of them, we will look at the pros and cons of using each method.
In this post, we will look at how to implement authentication using the most basic authentication tool available for us in the browser which is the HTTP Basic Access.
About HTTP Basic Authentication
HTTP Basic Authentication is a mechanism in which the server challenges anyone requesting for information and get a response in the form of a username and password. The information the server receives is encoded with base-64 and passed into the Authorization header.
When the client makes a request to a resource on the server that required authorization, the server sends a response with a 401
status code accompanied with a WWW-Authenticate Basic
header. Most browsers handle this response by requesting a username and a password from the user.
When the web client obtains the username and password, it sends a response back to the server with ah Authorization header in the form of Authorization: Basic username: password
.
However, as I mentioned earlier, the username and the password provided by the client is only encrypted with base-64. This approach of authenticating users is not recommended since information exchanged between both parties could be intercepted when the connection between the two is not secured. The HTTP Basic authentication is only secure when the connection between the client and the server is secure.
For more detailed information on the HTTP Basic Authentication see RFC 2617, HTTP Authentication: Basic and Digest Access Authentication
Now that we have had an introduction on how basic authentication works let's implement it in NodeJs.
Setting up the development stage
Before we go ahead and write any code, we will first install all the dependencies for our application. The first dependency to install is obviously expressjs, then we will add morgan.
A quick note about morgan and body-parser; morgan is an HTTP request logger middleware for nodejs which you can read more about here.
We then install all the dependencies in one line with this $ npm install express morgan body-parser --save
.
Bootstrap Server
After installing all the dependencies, let's add a new file index.js
in our working folder. This is where we will run the show.
First, let's bootstrap the server. We do this with the following lines of code.
const http = require('http');
const express = require('express');
const morgan = require('morgan');
const app = express();
app.use(morgan('dev'));
const server = http.createServer(app);
server.listen(3001,'localhost',()=>{
console.log("Server is live at localhost:3001");
});
Save the file and with your terminal, make sure you are in the working folder run $ node index
. You should see Server is live at localhost:3001
in your terminal. This is a sign that the server is up and running but we do not have any route yet. So that is what we will do next.
...
app.use(morgan('dev'));
app.get('/',(req,res)=>{
res.statusCode=200;
res.end("Welcome to your express app!")
})
const server = http.createServer(app);
...
The code above adds a default route to our application, which displays a message when we navigate to it. Stop the server in your terminal and start it again. This time around we should be able to navigate to our localhost at the port 3001 in our web browser http://localhost:3001
and see the message Welcome to your express app
.
Let's add another route, where we will put a secret code for only members who are authenticated. The route of our secret code will be http://localhost:3001/secret
...
app.use(morgan('dev'));
...
app.get('/secret',(req,res)=>{
res.statusCode=200;
res.end("******")
});
...
const server = http.createServer(app);
...
Since this is secret information we do not want everyone to access it, but only users who can authenticate themselves.
We will go ahead and implement our authentication in the next section.
Add HTTP Basic Access Authentication
Adding the authentication to a route is quite simple. We add it to as a middleware, which is then checked before proceeding to the route.
We add middleware in express with the use()
method. Hence any route that resides after the call to the use()
method is only accessed if the request passes the checks in the middleware.
To add our middleware, we will create an auth.js
file in our working directory where we will implement our authentication.
Add the following code to the auth.js
file.
function auth (req, res, next){
var authHeader = req.headers.authorization;
if(!authHeader){
var err = new Error('You are not authenticated')
res.setHeader('WWW-Authenticate','Basic');
err.status = 401
next(err)
}
var auth = new Buffer(authHeader.split(' ')[1], 'base64').toString().split(':')
var username = auth[0]
var password = auth[1]
if(username == 'edemone' && password =='secret'){
next();
}else{
var err = new Error('You are not authenticated')
res.setHeader('WWW-Authenticate','Basic');
err.status = 401
next(err)
}
}
module.exports = auth;
The portion of code above first checks for the Authorization header, and responds with a 401 status code and a WWW-Authenticate Basic header when the request coming from the client has no Authorization header.
We, however, continue to check the Authorization header in case we have one, to see if it contains a username and password and check it against the expected username and password. In this case, we are using a hardcoded username and password, but we could have implemented a check against information in a database.
When the check is OK the user is granted access, otherwise, we throw an error accompanied with the WWW-Authentication Basic header and a 401 status code.
There is now the need to attach our authentication check to the /secret
route. We do this in the index.js
file.
...
const morgan = require('morgan');
const auth = require('./auth');
...
app.use(auth)
app.get('/secret',(req,res)=>{
res.statusCode=200;
res.end("******")
});
...
Adding the app.use(auth)
before the /secret
route ensures that no access is granted unless the user passes the above middleware.
After restarting our server, when we try to access http://localhost/secret
we will be presented with a prompt in the browser requesting for the user to provide a username and a password. When the user enters a username and password that match the expected, the user is sent to the resource requested.
Conclusion
In this first post of a series on Authentication in NodeJS, we looked at using HTTP Basic Authentication to check for users who try to access a protected resource on the server. Information sent from the web client is encoded with Base-64. Though using HTTP Basic Authentication is not recommended, because information exchanged between the client and the server could be intercepted. In our next post, we will look at how to implement the same authentication using cookies and sessions.
Thanks for reading. Kindly put your thoughts in the comment section. This article was originally posted on my blog
Top comments (4)
Hi Edem,
Loved your post.
I also wrote a similar post on Authentication in Nodejs. Feel Free to check it out :
Authentication in NodeJS With Express and Mongo - CodeLab #1
Basic Auth is safe only when using it over https, same when using API keys. I prefer api keys because they can also be used to limit usage per key. I also detailed how to use jwt tokens: Securing a Node.js API: A Simple Guide to Authentication
Thanks for the insight. Explanation is really clear ..
the browser prompts the user for credentials once.
For how long does the browser send credentials along with each request.
Where does it store them?