There was a time when the only way to authenticate yourself with an application was by providing your credentials (usually a username or email address and a password) and a session was then used to maintain user state until the user logged out. A little while later, we started using authentication APIs. And in yet more recent times, JWTs, or JSON Web Tokens, have been increasingly used as another way to authenticate requests to a server.
In this article, you’ll learn what JWTs are and how to use them with PHP to make authenticated user requests.
JWTs versus Sessions
But first, why are sessions not such a good thing? Well, there are three key reasons:
- Data is stored in plain text on the server.
Even though the data is usually not stored in a public folder, anyone with sufficient access to the server can read the contents of session files.
- They involve filesystem read/write requests.
Every time a session starts or its data is modified, the server needs to update the session file. The same goes for every time the application sends a session cookie. If you have a large number of users, you can end up with a slow server unless you use alternative session storage options, such as Memcached and Redis.
- Distributed/Clustered applications.
Since session files are, by default, stored on the file system, it’s hard to have a distributed or clustered infrastructure for high availability applications — ones that require the use of technologies such as load balancers and clustered servers. Other storage media and special configurations have to be implemented — and be done so in full awareness of their implications.
Now, let’s start learning about JWTs. The JSON Web Token specification (RFC 7519) was first published on December 28, 2010, and was most recently updated in May 2015.
JWTs have many advantages over API keys, including:
- API keys are random strings, whereas JWTs contain information and metadata. This information and metadata can describe a wide range of things, such as a user’s identity, authorization data, and the validity of the token within a time frame or to a domain.
- JWTs don’t require a centralized issuing or revoking authority.
- JWTs are OAUTH2 compatible.
- JWT data can be inspected.
- JWTs have expiration controls.
- JWTs are intended for space-constrained environments, such as HTTP Authorization headers.
- JWTs are represented using Base64url encoding
What Does a JWT Look Like?
Here is a sample JWT:
At first glance, it appears that the string is just random groups of characters concatenated with a period or dot character. As such, it may not seem very different from an API key. However, if you look more closely, there are three separate strings.
The JWT Header
The first string is the JWT header. It’s a Base64, URL-encoded JSON string. It specifies which cryptographic algorithm was used to generate the signature, and the token’s type, which is always set to
JWT. The algorithm can be either symmetric or asymmetric.
A symmetric algorithm uses a single key to both create and verify the token. The key is shared between the creator of the JWT and the consumer of it. It’s essential that you make sure only the creator and consumer knows the secret. Otherwise, anyone can create a valid token.
An asymmetric algorithm uses a private key to sign the token and a public key to verify it. These algorithms should be used when a shared secret is impractical or other parties only need to verify the integrity of the token.
The JWT’s Payload
The second string is the JWT’s payload. It’s also a Base64, URL-encoded JSON string. It contains some standard fields, which are referred to as “claims”. There are three types of claims: registered, public, and private.
Registered claims are predefined. You can find a list of them in the JWT’s RFC. Here are some commonly used ones:
iat: the timestamp of token issuing.
key: a unique string, which could be used to validate a token, but goes against not having a centralized issuer authority.
iss: a string containing the name or identifier of the issuer. Can be a domain name and can be used to discard tokens from other applications.
nbf: a timestamp of when the token should start being considered valid. Should be equal to or greater than
exp: a timestamp of when the token should cease to be valid. Should be greater than
Public claims can be defined as you see fit. However, they can’t be the same as registered claims, or claims of already existing public claims. You can create private claims at will. They’re only for use between two parties: a producer and a consumer.
The JWT’s Signature
The JWT’s signature is a cryptographic mechanism designed to secure the JWT’s data with a digital signature unique to the contents of the token. The signature ensures the JWT’s integrity so that consumers can verify it hasn’t been tampered with by a malicious actor.
The JWT’s signature is a combination of three things:
- the JWT’s header
- the JWT’s payload
- a secret value
These three are digitally signed (not encrypted) using the algorithm specified in the JWT’s header. If we decode the example above, we’ll have the following JSON strings:
The JWT’s Header
"alg": "HS256", "typ": "JWT"
The JWT’s Data
"iat": 1416929109, "jti": "aa7f8d0a95c", "scopes": [ "repo", "public_repo" ]
Try out jwt.io for yourself, where you can play around with encoding and decoding your own JWTs.
Let’s Use JWTs in a PHP-based Application
Now that you’ve learned what JWTs are, it’s now time to learn how to use them in a PHP app. Before we dive in, feel free to clone the code for this article, or follow along and create it as we go.
There are many ways that you can approach integrating JWTs, but here’s how we’re going to do it.
All requests to the application, except for the login and logout page, need to be authenticated via a JWT. If a user makes a request without a JWT, they’ll be redirected to the login page.
authenticate.php, in our application. The endpoint will then extract the credentials (a username and password) from the request and check if they’re valid.
If they are, it will generate a JWT and send it back to the client. When the client receives a JWT, it will store it and use it with every future request to the application.
For a simplistic scenario, there’ll only be one resource the user can request — a PHP file aptly named
resource.php. It won’t do much, just returning a string, containing the current timestamp at the time of the request.
There’s couple of ways to use JWTs when making requests. In our application, the JWT will be sent in the Bearer authorization header.
If you’re not familiar with Bearer Authorization, it’s a form of HTTP authentication, where a token (such as a JWT) is sent in a request header. The server can inspect the token and determine if access should be given to the “bearer” of the token.
Here’s an example of the header:
Authorization: Bearer ab0dde18155a43ee83edba4a4542b973
For each request received by our application, PHP will attempt to extract the token from the Bearer header. If it’s present, it’s then validated. If it’s valid, the user will see the normal response for that request. If the JWT is invalid, however, the user won’t be allowed to access the resource.
Please note that JWT was not designed to substitute session cookies.
PHP Authorization with JWT (JSON Web Tokens)