APIs and Laravel: Part 1 — JWT Primer

Elpsy
6 min readOct 20, 2016

--

In this post, we will discuss the JSON Web Token standard, how to create tokens, and some of the benefits provided by implementing JWT within our application.

With Laravel 5.3, the contributors introduced the option to use the new Passport library to spin up an OAuth server in minutes. However, when it comes to typical applications, it may not be appropriate to throw an entire OAuth framework on top of your application. In fact, implementing OAuth authentication can actually be less secure if the author does not fully understand the framework and standard. So what other options are available for our API authentication? Well, there’s the JSON Web Token which will be more than enough to power our API.

JWT is an authentication protocol, unlike OAuth which is a framework, that allows two parties to securely transfer information in JSON format. It works similar to a session cookie in the sense that the client will be submitting a token as a cookie, JSON, HTTP header or query param which can be verified by the server. However, unlike session cookies, it does not require the server to maintain a session. This increases the statelessness of our APIs. In addition, the information is only secure in the fact that the creator can verify that the information has not been tampered with. The actual data is available to the client or anyone who compromises the token.

What does it look like?

A JWT contains three sections separated by a “.” and is delivered in an aaa.bbb.ccc format. Each section of the token serves a different purpose.

The Header

The header is the first portion of the token and contains two pieces of information: the type of token, and the hashing algorithm. Below, we declare the type (‘typ’) to be JWT and algorithm (‘alg’) to be HMAC SHA256.

{ 
“alg”: “HS256”,
“typ”: “JWT”
}

The header can also include claims such as content type (“cty”) and encoding (“enc”).

The JSON header will be base64 encoded and provides us with the first portion of token.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.bbb.ccc

The Payload

The payload contains the data which we want to securely share between parties. The payload can consist of three different types of data: Reserved Claims, Public Claims, and Private claims.

{
“sub”: “432”,
“name”: “Bobby Tables”,
"role": "student"
}

Reserved Claims

The “sub” key is an example of a reserved claims. In this case, it could represent a MySql id, uuid, or some other identifier. How you choose to implement the subject is up to you. Reserved Claims are key/values which have been agreed upon by the authors of the standard as being reserved for specific purposes. Other reserved claims can include expiration date (“exp”) or the token issued time (“iat”).

Public Claims

The “name” key is an example of a Public claim. Public claims are key/values whose meanings are generally understood as they are registered with the IANA JSON Web Token Registry.

Private Claims

Finally, the “role” key is an example of a private claim. It’s a custom claim that the creator (server) and the consumer (client) agree upon as being valid. Because we are writing the API as well as the front end application, the consumer really doesn’t have much say in this matter.

However, if our API were public, we would need to explicitly document our private claims so that the consumers understands their purpose. As well, be careful to avoid naming collisions with Reserved and Public claims as it will cause confusion for our consumers and may break any third part implementations within our application.

The payload is base64 encoded and concatenated onto the token.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MzIiLCJuYW1lIjoiQm9iYnkgVGFibGVzIiwicm9sZSI6InN0dWRlbnQifQ.ccc

The Signature

Right now, you may be thinking “How is this secure? All we’ve done is base64 some JSON?” The most important portion of the token is the final section — the signature. The signature is how we verify that the token has not been tampered with. We calculate the signature by taking the first two sections, concatenated by a “.”, and hashing it with our secret.

$header64 = base64_encode($headerJSON);
$payload64 = base64_encode($payloadJSON);
$signature = hash_hmac("sha256", $header64 . "." . $payload64, $secret);
$signature64 = base64_encode($signature);
$token = $header64 . "." $payload64 . "." .$signature64;

Above we do the following:

  • Base64 encode our header
  • Base64 encode our payload
  • Generate our signature by concatenating with a the encoded header and payload with a “.” and perform a hash of the string with our secret key.
  • Base64 our signature
  • Concatenate the encoded header, payload, and signature separated by a “.”.

It’s important to note that the header and payload are not encrypted. They are still base64 encoded. So whether it is on the server or on the client, we can still read the $header and $payload. It is important to understand this concept as it will effect which information you deliver to the client.

Authorization

When a user submits a request with a JWT attached as either a cookie, JSON, HTTP Header, or a query parameter, the server will base64 decode the header in order to know which hashing algorithm (“alg”) was used to sign the token. Afterwards, it will verify that the header and payload have not been changed by attempting to reproduce the hash with the original secret. This hash cannot be forged as long our secret is cryptographically secure, securely stored, and generated with an appropriate hashing algorithm. It goes without saying, but the client should never have access to the private key. Remember, the already have access to the payload as it is only base64 encoded.

After verifying the token as authentic we base64 decode the payload (claims) to do some additional verification. For example, we may check the expiration claim (“exp”) of the token to determine whether it is still valid. Or we could compare it to a blacklist to see if the token is no longer valid. This is very common as tokens are often refreshed with the old tokens blacklisted.

Security

JWT requires HTTPS. Use it. If a token is compromised, the account is compromised. However, JWT does offer some advantages besides improving statelessness. JWT provides a standard for passing additional information that can be used in authorization such as the expiration “exp” claim. You can issue many tokens with short expiration dates in order to invalidate compromised tokens quickly.

Advantages

  • Scalable: The server only needs to store one key for all users as opposed to storing a session for every single user.
  • Transferable: Multiple servers can authorize a user without having to share session state or a common session database.
  • Dynamic: Not all clients can store or handle session cookies. JWT provides a dynamic delivery process as they can be attached to a request in many different ways.
  • CSRF Protection: Because a JWT is difficult to guess, it is not required to send a csrf token. However, if the jwt token is stored locally (cookie, localStorage), it may still be necessary to send a csrf as the token may be vulnerable to XSS.

Alright

JSON Web Tokens are pretty neat. They are compact and cheap and provide additional information about the authenticated user that would be more difficult to obtain with session cookies. In the subsequent posts in this series, we will implement JWT in Laravel, construct an API and simple client, use repositories, transformers and presenters in our API, discuss OAuth, and refactor our code to implement a self consuming API using Passport.

Part One: JWT

Part Two: Laravel and Tymon JWT

Part Three: JWT Auth Guard

Part Four: Building our API

Part Five: A Vuejs Front End

Part Five: Repositories, Transformers and Presenters

Part Six: What is OAuth?

Part Six: Consuming Passport

Resources:

JWT: Introduction to JSON Web Tokens

Scotch: The anatomy of a JWT Token

A Better Post on JWT

Token Registry

The JWT Spec: RFC 7519 (it’s not that long and definitely worth reading)

Notes:

Fun fact: “JWT Token” is the same as saying “JSON Web Token Token”.

Follow me on twitter? Maybe? @3lpsy

Feel free to comment down below or reach out to me on twitter if you have any comments, corrections, or suggestions for how I can improve this post.

Edit: Changed $signature to $signature64 in code block

--

--