Konrad Schultz
Feb 19 · 7 min read

Authentication and authorization are at the core of most web applications. They are mostly relegated to third party libraries to handle for us. Which is a good thing! In this article I will focus on understanding web application authentication. I will work through a few different types landing on what we use at Riipen, token based authentication with JSON Web Tokens (JWTs).

No developer wants to spend days debugging authentication errors when there is business logic to code! Authentication and authorization are often seen as black boxes. The less you have to touch them the better. Instead of developing a deep understanding of the ideas behind authentication it is tempting to poke at things until they work. But we are better than that, we can read medium articles and form opinions for ourselves! Introspection intensifies.

Note: Use your framework or library of choice for authentication. I am not suggesting using a library or framework is bad. Although it is helpful to understand what is going underneath the library to be able to debug problems when they occur.

What is the difference between authentication and authorization? The way I think about it is that authentication is forcing users to prove their identity and authorization is restricting what a user is allowed to do. For example in order to authenticate myself I provide a username and password. If I try to view a resource the server should check if I am authorized to do so.

Let’s focus on authentication. There are a few common ways to do this and they are often subject to different sets of implementation details. We will look at basic authentication (username and password), session authentication and token authentication (specifically with JWT but any token will do).

Note: Please use TLS. If you are transmitting passwords or tokens or anything important over HTTP (rather than HTTPS) you are always going to be subject to man in the middle attacks.

HTTP Basic Authentication

To ensure the resources on Riipen are secure, a user needs to be identified on almost every request. The most simple way to do this is for a user to send identifying information (their username and password) on every request. This is called HTTP Basic Authentication. For example, if they want to create a project on Riipen they will have to prove who they are. Here is a fictitious example (Riipen doesn’t use basic auth):

curl \
-H "Content-Type: application/json" \
-H "Authorization": "Basic myUsername:myPassword" \
-d '{"name":"my new project"}' \
-X POST http://localhost:3002/private/v1/projects

Perfect! We just created a project on Riipen! Without that authorization header our server will return an HTTP 401 unauthorized error. Because the web app needs to provide the username and password on every request we can either:

  1. Prompt the user for their credentials before every request (wow that sounds horrible)
  2. Store the username and password somewhere clientside (possibly yikes)

Option 1 is a bit facetious, this isn’t really an option if we want people to use our site and remain sane.

For option 2 if we store the user’s password anywhere that is accessible to third party JavaScript this could be bad. Especially because people have a tendency to use the same password over and over again. If there is any un-trusted code running on your site or an XSS vulnerability that password will get stolen and your users will be unhappy. We can store it as an http-only cookie but then we have to deal with Cross Site Request Forgery. This problem is not unique to HTTP Basic Authentication and remains a problem using tokens. It is exacerbated by HTTP Basic Authentication because an attacker having a token is bad, but an attacker having a plaintext password is worse.

There is another minor issue with HTTP Basic Authentication. You are a good web developer so you are storing your passwords hashed and salted with a sufficient rounds on your hashing algorithm, correct? Great! This means that on every request, a plaintext password is sent to the server. This password must be hashed and checked against the stored value. Hashing is fairly CPU intensive and adding it to every single request is not a great use of resources.

Sessions

An alternative to this is session based authentication. The way sessions generally work is the web app makes a single request with username and password (login) and receives a session id in return. This session identifier is saved as an http-only cookie. Future requests to the server with cookies will then contain this session id. Finally the server can look up which user was associated with that session id when the session was created.

For this implementation it is important to have opaque, “hard to guess” session ids so that they cannot be guessed or enumerated by attackers. This is an improvement over HTTP Basic Authentication in some ways but adds additional complexity. You need to store this session information somewhere (usually a database) and retrieve it when a user needs to be authenticated. Often session based authentication is built into whatever framework you are using and is fairly easy to adopt.

Tokens

One more alternative is token based authentication. Tokens behave very similar to sessions in some ways. You make a single request with your login credentials and receive a token in return (instead of a session id). You save the token somewhere and then attach the token to every request so that the server can authenticate you on each request.

The difference between the two methods is that we want to store information in the token to prevent the server from having to look up session information. This can save some server complexity as well as runtime. Another benefit is that the server that issues the token can be different than the server that consumes the token. This means there is no central coupling to some session implementation. An issue with storing information in the token is that you can never trust the client (web app). So we need a way to verify that the token originates from a trusted server and has not been tampered with.

Luckily we have the magic of cryptography! We can use symmetric or asymmetric cryptography and each fits certain use cases better.

A key part of token authentication is to generate a token with the information that is required to identify a user. This is usually referred to as the payload. Often times this can be simply a user id but can also contain claims about what a user is authorized to do. Authorization is beyond the scope of this article though.

The encryption can take many forms, the payload can be plaintext and a signature can be added to the end, the whole token can be encrypted, etc. JWTs are all the hype right now and they have a 3 part token. The header, payload, and signature. It takes the form:

header.payload.signature

The header is plaintext and indicates some meta information such as what algorithm was used to sign the token. These fields are optional but can help with systems meant to accept many JWTs from many sources.

The payload is also usually plaintext and contains the meat of the token. It is common to include expiration date, issued date, and other fields. Riipen’s JWTs look something like:

{
"aud": "Audience (Resource server)"
"azp": "Authorized Party (Resource or third party server)"
"exp": "Expiration Time"
"iat": "Issued At"
"iss": "Issuer (Authentication server)"
"jti": "JWT ID"
"sub": "Subject (User ID)"
}

The signature is a cryptographic signature generated using a secret and the payload. This means that we can check if the payload has been tampered with by re-signing the payload and comparing to the signature. If they are different something is wrong!

Symmetric

Symmetric cryptography can be used in cases where the same server is issuing and consuming the token. It also means clients or other servers cannot verify where the token came from. The complete steps are:

  1. The client sends a login style request with a user name and password.
  2. The server verifies the password is correct and generates a token to identify the user.
  3. The server signs the token using a secret known only to the server.
  4. The server sends the signed token back to the client.
  5. The client attaches this token on future requests.
  6. The server verifies the token with its stored secret and now knows which user is making the request.

Asymmetric

Asymmetric cryptography can be used where the token is issued by one server but eventually consumed by a different server. It is also good to use if a client wants to verify the authenticity of a token it receives (possibly from a third party). The complete steps are:

  1. The client sends a login style request with a user name and password.
  2. The issuing server verifies the password is correct and generates a token to identify the user.
  3. The issuing server signs the token using the private key known only to the server.
  4. The issuing server sends the signed token back to the client.
  5. The client attaches this token on future requests.
  6. The consuming server verifies the token with the public key known to all and now knows which user is making the request.

I have glossed over a lot of cryptography so if you want to convince yourself this works and is possible to establish trust in this way you should read about public key cryptography and the difference between symmetric and asymmetric encryption.

Parting Words

Token based authentication is also central to a few of the authentication methods used in the OAuth 2.0 specification. Token authentication might not fit your use case but it is worth thinking about. At Riipen we chose to migrate to token authentication to support an open version of our API and specifically the ability for users to authenticate third parties to make requests on their behalf.

In this article we explored a progression of authentication, from basic auth, to sessions, to tokens. Each one has trade-offs and it is important to be aware of the drawbacks and possible vulnerabilities of each. We choose to use JWTs because they are standardized, well supported, and somewhat self-documenting. There are many other token types and even more custom token implementations so choose what is right for you and your organization!

Riipen Engineering

A discussion of Riipen engineering and technology

Thanks to Jordan Ell

Konrad Schultz

Written by

Lead Software Developer at Riipen

Riipen Engineering

A discussion of Riipen engineering and technology

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade