After spending some time learning how to build Thriftly APIs, it’s time to think about the next steps - how to secure your API and implement a login system for your users.
If your data doesn’t seem sensitive, you may be thinking that security might not be as high of a priority for your API. However, whenever you have user login data, your data is sensitive. Even if most of your data would be safe to be publicized, your users’ email address and password hashes could be used maliciously for spam or accessing sensitive financial data on other websites. Therefore any application with user data has an obligation to protect that data with the utmost care.
We will help you do just that by discussing some important security concepts as well as the Thriftly specifics for implementing them.
There are several steps to be taken in order to secure your API. We will focus on user authentication as the first steps after getting set up with Thriftly.
Authentication vs Authorization
Authentication and authorization are both integral yet distinct components in security. Authentication is concerned with the process of verifying that an entity is who they say they are, whereas authorization involves determining what kinds of access a verified entity has to resources, such as the ability to create records or delete data, etc. Authentication is most commonly done with a username and password for credentials and this will be our focus. As for authorization, that’s a topic for another article.
User vs Server
Different measures should be taken to secure communications from different types of entities, i.e. users vs external servers. It may be tempting to reuse user based credentials for server access, but this can lead to vulnerabilities. For example, if an admin account is used for an external server, it may have access to more resources than needed. There are a variety of server based security measures (such as OAuth, API keys, and JWTs) that can be used instead. For now, we will focus on securing user based interaction as a first step.
There are several things to consider when integrating user authentication. We will focus on maintaining secure credentials and tracking the user session state.
While encryption may seem like a good solution for password security, passwords should not be stored, even if they are encrypted. There are a variety of vulnerabilities with encryption (e.g. padding oracle attacks) as it is prone to interception and is a reversible process. Instead, password hashing should be used - it utilizes a hash algorithm and is practically irreversible. (Some secure hashing algorithms to use include: scrypt, bcrypt, PBKDF2, and higher levels of SHA). The first step of the hashing process is to generate a salt - a random, fixed-size string that should be unique for each user. (This can be done using a cryptographically safe random number generator, such as the RNGCryptoServiceProvider class in C#). The salt helps protect against rainbow table attacks, which involves using precomputed tables of reversed hash values. For the next steps of the hashing process, append the salt to the plaintext password and then input the combined string into a hashing algorithm, with the output being the hash. Therefore whenever a new password needs to be created, e.g. for user sign up or password reset, you would first generate the salt, create the hash, and then store both in your database (as they will be needed for verifying login attempts later).
Another important part of password management is to provide users with a password reset option. The implementation of this feature should be separated into two distinct parts - the reset request and the actual password update. In other words, the current password should not be invalidated until a request is actually consumed by the appropriate user. Some reasons for this are that the request could be made by other individuals (i.e. someone other than the user account owner) or that the user may remember their password after making a request.
API’s need to manage the state of user interaction. This involves whether a user has been authenticated, what to store for identifying a logged in user, and how long before a user will need to re-authenticate. (This is also needed for authorization). There are many different ways of tracking state, but we will focus on the one Thriftly provides - JSON Web Tokens (or JWT’s for short). They consist of Base64Url encoded JSON data regarding header and session information. JWT’s are generated by the server but stored on the client and used as a header token in API requests. The client is unable to tamper with a JWT as the token is signed either with a secret using the HMAC algorithm or with a public/private key pair using RSA or ECDSA to verify the data integrity. However, while the signature protects the integrity of the data, it does not prevent external eyes from seeing the payload content. Therefore, it is important not to store anything sensitive in a JWT. (To read more about JWTs, see jwt.io).
Implementing in Thriftly
Now that we have covered some security concepts, we’ll explain how functions are secured with JWTs and how to implement this in Thriftly.
When you are using JWTs with Thriftly, your API service functions can either be secured or unsecured. Unsecured functions do not check or require any JWTs before allowing access to themselves, whereas secured functions do both before they can be successfully called. For a user to access a secured function, they must first call the login function (which should be marked as such so that it can be accessed without a token) to receive a JWT. When secured functions are called, Thriftly handles the heavy lifting initially (meaning no code is needed for this step) by first checking for a JWT and then, if one is present, verifying its signature. If the request passes this stage, your auth method (which you will need to implement) will be called. This is used to check and manage the user session state. For example, with a JWT with an expiration time field, your auth method could give more time to the token or throw an exception if the JWT has expired. Only once the request has passed this step, will your published API function actually be called.
How to Implement
In order to implement JWTs for a secure login system with Thriftly, you will need to have the following things:
Below is an overview of how to add these items. For the full details please see the Thriftly docs.
First, you will need to create a struct in your code to hold fields pertaining to a JWT payload. This struct must be placed in a scope accessible to your login and auth methods as well as to any other methods that may need to use the JWT.
You will need to implement an auth method. It must be specified with a Thriftly auth attribute. The method both receives and returns a JWT struct. (See Thriftly docs for the full function prototype). Here is where will you throw exceptions for anything your API deems invalid as well as where you will perform any updates to the JWT state.
You will also need to implement your login method. It must be tagged with a Thriftly attribute noting the method is unsecured and for login (see docs for specifics). Here is where you will check the password attempt by creating a new hash by combining the supplied password with the stored user’s salt and comparing that hash to the one stored in the database. If it is a match, this method will need to return a JWT struct specific to the user.
Finally, you will need to add a configuration for your JWT’s secret/key to Thriftly. This is done in the Thriftly Configurator (for Thriftly Developer, this is the window that appears when you run your code in your dev environment). Thriftly is able to generate keys for you, again saving you another step. For details on how to add a JWT config, please see the Thriftly docs.
Now that we covered some first steps, you can read the Developer Docs to learn more and start securing your applications with the help of Thriftly.