Adding Middleware to Protect Routes in MEAN Stack
In our previous section, we successfully got our web token. In this section, we will send this web token back to our client. We can receive it in our Angular app. We will use that token to control access on the backend and deny it for any requests that don't have a valid token. We then want to be able to still reach these requests by actually adding the token to requests sent by Angular to such protected endpoints. We will use the following steps to do that:
We start verifying the token on the server. So, we check whether a valid token is attached to an incoming request. We will do that for some of our routes. But the question is which routes should be protected? On the backend, the user-related routes for logging in and for signing up should not be protected. Any user of our web page should be able to reach these routes. Any user should be able to send requests to these routes because the users interested in signing up and logging in are not authenticated.
Now for posts, there we have some routes that should be protected like, for example, the post route for creating a new post or the put route for editing one and the delete route should also be disabled for users who are not authenticated. So, we have to add something to these routes to check whether an incoming request has a valid token or not.
For that, we can use an approach used for the image upload. There we add extra middleware to our route setup. The route is constructed to first have a look at the path, and then we use the multer middleware to extract any files that are part of the incoming requests. We can add as many middlewares as we want, and the idea is to add yet another extra middleware that checks whether we are authenticated or not. This will be a middleware we create on our own, and for that, we will use the following steps:
2) In this file, we will import our jwt package, and with it imported, we will then export something in that file because we want to use that middleware in other files, in my route files to be precise. We will use the export syntax with exports and set this equal to a function because middleware is just a function that receives our three well-known arguments, i.e., the requests, a response, and a response object. This would allow us to create a response and next which we could call if the request should be allowed to continue.
This is a typical middleware in Node/Express. It is just a function that gets executed on the incoming requests. That is also what multer is doing behind the scenes.
3) Now, we are creating an extra function that runs before our final function. In this middleware, we try to get the token from the incoming request and store it in this constant. It is up to you where you want to get that token from. We could request or parse it from the URL as a query parameter. So, we could say our token is stored in our query parameters, and there we have an auth field. But, we want to accept it as a header. So, we will look into the headers of our incoming request, Express gives us the headers object for that, and there we expect to have an authorization header.
Now, we can theoretically expect any header we want, but authorization is a typically chose name for attaching authorization information to a request.
4) Now, there we expect to get our token and actually the pattern we typically use it that we assign a value to that header which uses a word, i.e., Bearer and then the token, and that simply is a convention to indicate. Well, that we basically added such a token to our authorization because there are authentication alternatives where we don't use a token like this.
The bearer keyword is totally up to you, it is optional, but it is often chosen. We see that on other APIs too, and therefore we will use it too. Since the token and then it is the second part of that string. We will split the authorization header on the whitespace it contains.
So, after this bearer word and we are then interested in the second value, we get out of there, so since we started index with zero, the second has the index one because that will be the part after the whitespace, so our token. That is just a convention that we can edit, by the way. Of course, we don't have to expect this bearer word, which then would mean we couldn't omit that split logic.
5) Now, this gives us the token, and we could check whether this is undefined or not because it should not be undefined if we have a token. Now, in general, this could also just fail. If we get no authorization header, calling split would simply throw an error. So, we will wrap it into a catch block, and it means we try to do that split, but it can fail, and we will catch the error if it fails. If it fails, we also know that we don't have a token, and we are not authenticated. In this case, we will set response with a status of 401 for not authenticated and then send back some JSON data if we want.
6) Now, we will verify that token, and for that, we will use the jwt verify method that will also throw an error if it fails to verify. So, this will also wrap into a try-catch block. In this verify method, we will pass the token we parsed from the incoming request. Now for verifying to work, we need to pass one other important piece of information, i.e., secret string, which we also used for creating the token.
7) If it doesn't fail, we want to call the next() method, and after that, the request will be able to travel on.
8) To use that middleware, we have to import it into our posts route file because we don't plan to use that middleware in the user.js file. So, we will go back to our posts.js file, and we add a new import checkAuth.
9) Now, we add that middleware by going to the routes we want to protect and add it as an extra argument after the path, but before all the other logic we want to execute. So, for creating the post, for example, we don't even want to try to extract the uploaded image if we are not authenticated. So, we will run this before running the multer middleware. Now, for putting, we have the same logic after the path before we try to get the image.
Note: We don't execute this function. We just pass the reference to that function we created to that middleware function, and Express will execute it for us when a request reaches it.
10) We mentioned that we want to allow the fetching of all posts and a single post, so we didn't add the middleware there, but we will add it for deleting.
In the next section, we will attach the token to requests sent from the frontend and see that we make it work again and then with authentication.