API Authentication And Security
The goal of this section is to learn how to lock down and secure an API that is currently open. That means securely storing passwords, setting up rules on what can and can't be read by a user, requiring them to authenticate themselves to access resources, etc.
Securely Storing Passwords
In a naive API and database, passwords are stored as plaintext. That means passwords are exposed. If someone were to get their hands on the data, they could now break into a user's accounts.
The solution we'll be implementing is to store hashed passwords: values algorithmically generated from passwords that are one-way, i.e., can't be reversed or decrypted. The hashing algorithm library we'll be using is bcryptjs
:
Hashing in model middleware
Object modelers like Mongoose allow you to apply middleware that run before saving to the database. We can apply a hashing algorithm as middleware in a Mongoose schema:
Logging in Users
To log in users, we just need to
Find the user by email, and
Check if the given password matches the hashed password.
Pro tip: When throwing error messages related to authentication, you want to keep it generic so as not to give away too much information.
Authentication Tokens
As you build out an API, your endpoints will fall into one of two types:
Public routes
Examples: login and sign-up
Routes that require authentication
Examples: viewing your own tweets, creating a post
The way to enforce authentication is to use an authentication token. When the user logs in, they're given a token to give them access to closed routes.
JSON web tokens
A popular type of authentication token is the JSON web token.
A JSON web token has 3 components:
Header information
Displays metadata like algorithm used or type of token
Data
Information you pass into the algorithm gets stored here
Note: Your token will have
iat
(issued-at timestamp) and could haveexp
(expiry timestamp)
Signature
This is a secret key used in the algorithm to verify that the token was made by the server
We'll use the jsonwebtoken
library to implement our own JSON web tokens:
Generating, saving, and sending tokens
When a user logs in, you need to do the following:
Generate the token.
Save it in the database associated with the user.
Send the token back in the HTTP response.
Here's the code for steps 1, 2, and 3:
BONUS: How middleware work
As a preface to the next heading, we'll explain how middleware work in express:
A middleware is basically a function that gets called between when a new request is made and when a route handler processes and responds to it. It has access to req
and res
as normal, and it has a next
function that tells express to move onto the next middleware or the route handler.
Middleware are a great place to control the flow of your routes and to implement checks before handling a request--like authentication!
Authenticating tokens
When a user makes a request to a route that requires authentication, a smart design choice is to use a middleware to gatekeep your routes:
Validate that the token provided can be found in the database.
If the token is good, move onto the route handler.
Finally, pass the middleware to the routes that you want to lock down via authentication.
In the request header, the user must send the token they were issued.
Note: A good idea when creating your tokens is to include the user's _id
in the payload. That way you can use the _id
to query for the token within the user's data--like this:
Logging out Users
Because our endpoints can include an authMiddleware
that passes user
and token
information, setting up a logout endpoint is a breeze:
Simply remove the token from
tokens
, thenSave and send
200
response.
Hiding Private Data
In our naive endpoints setup, any query to user information automatically returns everything, including password and tokens.
One solution is to implement a getPublicProfile
instance method:
Pro tip: Whenever you send a response, express automatically serializes the object into JSON using JSON.stringify
. However, you can override what gets serialized by including a toJSON
method in the object. By doing this, the return value of toJSON
is instead serialized.
Here's a simple example:
Applied to removing private data...
Authenticating User Endpoints
Last updated