Securing REST APIs with Token-based Auth
Auth is a service layer that does authentication to identify if the user is who they claim to be and does authorization to check if the user has permission to access the specified REST API. The most common form of authentication is using a username and password, but it is cumbersome and a security issue to have embedded in an app or script. More commonly used form of authentication is using tokens that represent the users or clients.
Authentication is the method to verify the person is who they say they are. In the internet world, people will provide a username and password to access their account. A real world, imagine an office building with multiple tenants and a security guard at the entrance. In order to enter, the guard asks you who you are and inputs your name, company, and floor number into a computer in order to be verified who you are. The process takes about 30 seconds which isn’t too long for a single person. However, there’s a line of people behind you waiting to do the same thing, which turns into a very cumbersome and time consuming process for multiple people. If there are 10 people in front of you, it’ll take 5 minutes to reach the guard! There’s also a security concern of passing information back and forth in the open because you never know who is trying to eavesdrop to steal your information. The person behind you overheard all your information.
Instead, the building owner verifies the information of who you are and if you indeed work for the company that you say you work for. Then they issue a badge that has your name and company on it. The security guard looks at the badge and waves you through. The badge is very similar to a token that is used for authentication on the internet. Once a user verifies who they are with a specific set of information such as username and password, a token is issued to be used for any subsequent authentications.
Authorization is the method to verify if the person has access to a specific resource. Using the previous example of a work badge, the remaining information needed is the floor number that the person can access. Other forms of authorization can be security clearance, role, or access level. All of these different types of information translates into access control of areas. For REST APIs, authorization provides access control to specific end points that a certain user can access based on a specific information such as user role or even individual URLs.
Token-based Auth Solutions
Unique Random String
A random string token is a basic solution that hides user credentials but is hard to scale as it requires the server to do a database lookup for more information. It doesn’t scale well over time with more users, permissions, and APIs. As the number of API requests increases, it becomes more demanding on the server to do additional database lookups for each API accessed. This particular solution that is ideal for systems with a small set of users and APIs.
An encrypted string token is a solution where user credentials and other information is within an encrypted string. The string may be a json blob, key value pairings, or even a concatenated string. This server issues the encrypted string to the user after authentication and it decrypts the string when the user passes it with the API request. This particular solution helps the server make less database lookups. Depending on the encryption used, the performance may be worse than a database query as the server needs to decrypt the token for each request.
JWT — JSON Web Token
JSON Web Token embeds information in the token that saves the server from having to query the information for each API request. How it works is the user logs in with their user credentials, and the server returns a JWT containing information, called claims, about the user. There are standard claims such as expiry, issuer, issue time, and subject. Additional custom claims may also be embedded in the token to save the server from querying the database until the token expires. These tokens are digitally signed, which helps the server detect if someone has tampered with them. When the token is about to expire, the client asks for a new one through the a token refresh API.
One problem with JWT is how easy it is to read the claims because it is base64 encoded which makes the token’s contents easily decoded back into plaintext (human readable text). Anyone can read the token’s content containing the claims, which makes it a bad choice to put sensitive information in. A common solution to hide the information is encrypting the JWT claims or the entire JWT. Depending on the encryption used, the performance may be worse than a database query as the server needs to decrypt the JWT for each request.
Another problem with JWT is revoking the token because it can be used an unlimited time until the expiry. Setting a long expiry time (a day or longer) causes issues in scenarios such as the user being suspended. Another situation is a 3rd party person obtains the token and uses it. The best practice is to set the expiry to minutes in the future to reduce the window of opportunity and potential damage due to misuse.
Refresh and Access Tokens
Refresh and Access Tokens combines random string tokens and JSON Web Tokens for a secure and performant system. The random string token is the refresh token and the JWT is the access token. How it works is the user accesses the authentication API with the refresh token to obtain an access token to be used to access APIs. Only the refresh token requires database queries, and the access token contains authorization information. Until the access token expires, it can be used an unlimited amount. When the access token expires, the user obtains a new access token by using the refresh token through the authentication API to repeat the cycle. The refresh token does a database look up which helps with users who are suspended.
A slight variation for increased security is to make the refresh token usable only once (one time used token). Using the previous example, the authentication API returns both a new refresh token and an access token. The previous refresh token is invalid and cannot be use again. The user saves the new refresh token for later usage and uses the access token to access the APIs. When the access token expires, the user uses the authentication API with the new refresh token to repeat the cycle. Overall, this solution provides a secure and high performance solution for large systems.
Each token authentication strategy has its own pros and cons. The right token authentication will vary on your server needs. For servers with a small user base and REST APIs, a simple random string would be enough. For larger server, JWT is commonly used as it can store information within itself. For more secure server, Refresh and Access Token combines both the random string and JWT concepts.