Welcome back to our ongoing series on the Ktor framework and its impressive capabilities when it comes to web application security. In this article, we shift our focus from traditional form-based authentication to a modern, innovative one – JSON Web Tokens (JWT). JWT provides several key advantages that have led to its growing popularity. Firstly, its stateless nature promotes better scalability by eliminating the need to maintain user sessions. Secondly, JWT’s JSON structure guarantees cross-platform compatibility. This means your authentication mechanism works equally well for web apps as it does for mobile apps or other systems. Lastly, JWT proactively increases the security of your applications, preventing attacks like CSRF. So, join us today as we delve into the world of JWT, exploring its mechanics and benefits.


This article is a part of an interactive, hands-on tutorial. You’re invited to check out the project and code along with me. For the sake of maintaining clarity and readability, I’ve divided the content into two sections. In this first part, we delve into JWT as a concept. By the end of this post, you’ll have a comprehensive understanding of its various components. This knowledge will equip you for the next post, where we’ll implement JWT authentication in Ktor.

Table of Contents

Benefits of JWT Authentication

JSON Web Tokens authentication brings in multiple benefits that make it an attractive choice for securing web applications. Its stateless nature allows high scalability since there’s no need to store user sessions on the server, which greatly simplifies the architecture and decreases resource consumption. JWT is based on the universally recognised JSON standard. This makes it applicable across a variety of platforms and client types, including web and mobile apps, as well as backend services. Last but not least, the inherent structure of a JWT, being divided into a header, a payload section, and a signature, provides a robust security layer. It includes necessary user information and an encrypted signature that safeguards against tampering attempts, offering excellent protection against security threats such as Cross-Site Request Forgery (CSRF) attacks.

How Does It Work?

JSON Web Tokens as a security standard make use of a secret key, which plays a critical role in ensuring the security and integrity of the token. Here’s how the authentication works.

JWT authentication mechanism
A secret key stored on the server plays an important role in the JWT standard.
  1. Authentication: A user logs into an application by providing valid credentials. This initial steps ensures a legitimate user access and user identification. The credentials are never passed around in the subsequent steps.
  2. Token Generation: The server creates a JWT. This token includes a payload which contains user information (claims), a header with information about the token type, the hashing algorithm, and a signature computed using a secret key.
  3. Token Delivery: The JWT is then encoded and sent back to the client.
  4. Token Storage: The client stores the token locally, often in local storage or as a cookie.
  5. Client Requests: The client includes the token in the Authorization header with any further requests to the server.
  6. Verification: When the server receives the request, it first verifies the JWT signature using the same secret key it used to sign the token. If the signature is valid, it means that the token has not been tampered with during transit.
  7. User Role Resolution: The server then parses the payload to identify the user’s permissions or role.
  8. Access Grant/Denial: Depending on the role and access rights saved in the payload, the server grants or denies access to the requested resources.

Stateless Authentication

It’s important to note that the authentication (and authorization) process is stateless. In other words, the server does not store user sessions or tokens. Instead, it relies on the client to present a valid token with each request. The server verifies this token and provides the appropriate response.

Also, the signature is critical to the security of the JWT. The secret key must remain, well, secret and safe on the server side. This ensures that if the token payload changes in any way (indicating tampering), the server can identify it during the signature verification step.

Know Your Realm

Realm is a term you will see a lot when using JWT. The realm essentially identifies the protection space. For instance, a realm allows the HTTP server to distinguish among various areas of protection or realms within a particular domain when communicating with the client. Understanding where the user is supposed to have rights to access can assist in managing user access and authorization.

Anatomy of a JSON Web Token

A JWT token consists of three parts.

The header typically includes the token’s type (JWT) and the signing algorithm used, such as HMAC SHA256 or RSA. It is a JSON object containing metadata about the JWT. For example:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

The payload contains the claims, which are statements about the entity (typically the user) and additional data. Claims can be registered, public, or private claims. The payload is also a JSON object. For example:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

Signature

The signature is created by combining the encoded header, encoded payload, and a secret using the algorithm specified in the header. The signature is used to ensure the integrity of the information within the JWT.

These three parts encoded in Base64 format, concatenated with dots form a JWT:

encodedHeader.encodedPayload.signature

Here is an example of a JWT token:

The coloured sections represent a header and a payload JSONs as Base64 encoded strings. The last part is a signature. Credit: jwt.io

Demystifying Token Claims

Claims are statements about an entity (a subject like a user) and additional metadata. They’re useful when we want to assess whether the user is eligible to access a protected resource. Claims are part of the Payload section. A JWT can contain three types of claims: Registered, Public, and Private.

Registered Claims

These are a set of predefined claims which are not mandatory but recommended. You can treat them as a set of standard, interoperable claims. JWTs can include any of the following claims:

  • the issuer (iss)
  • the subject (sub)
  • audience (aud)
  • expiration time (exp)
  • not before (nbf),
  • issued at (iat)
  • JWT ID (jti)

In this tutorial we will be using some of these claims. Let me explain a little bit closer what they are and how to use them.

Audience (aud)

The audience identifies the recipients the JWT is intended for. It’s a way of targeting where the JWT should be sent. If a JWT accidentally ends up at an unexpected system, or it is intercepted and resent, the server can check the aud claim and determine whether to process the JWT or reject it.

Issuer (iss)

The issuer is a case-sensitive string or a Uniform Resource Identifier (URI) that uniquely identifies the party who issued the JWT. In other words, it refers to the system or the server that generated and signed the JWT. By validating the iss claim, the recipient can ensure the token’s origin and confirm it was indeed issued by the expected party, thus raising trust in its authenticity.

Expiration Time (exp)

Essentially, this claim sets a time limit on the token’s validity. The value of the exp claim is a timestamp that must be later than the current time.

The exp claim is particularly valuable in managing the lifespan of a JWT, effectively preventing potential JWT token abuse. By designating an expiration time, we make sure the token cannot be used beyond a specified timeframe. This adds an extra layer of security to our authentication process.

Public Claims

These are custom claims, meaning they can be defined at will by those using JWTs. But to avoid collisions, the authors must register them in the IANA JSON Web Token Registry or define them as a URI that contains a collision-resistant namespace.

Private Claims

These are the custom claims created to share information between parties that agree on using them and are neither registered nor public claims.

Token Expiry and Renewal

JWTs usually include an expiration time (exp claim), which limits the lifespan of the token for security purposes. The expiration value indicates when the token should be considered invalid and is typically represented as a UNIX epoch timestamp. Managing token expiry is vital for limiting potential misuse of a token if it is intercepted.

When a JWT expires, the client would need to authenticate again and get a new token. However, in order to provide a smoother experience to the user, the token is renewed behind the scenes using a refresh token. This is a different kind of token with a typically longer expiration time whose sole purpose is to get a new access token without asking the user to manually authenticate again.

Here is an authentication flow that handles the JWT token expiry.

A refresh token allows for automatic re-authentication without disturbing the user.

The authentication flow is the same as described previously, except the server now creates a pair of tokens instead of a single token:

  • Access token: a JWT token that identifies the verified user and allows to make requests on the user’s behalf.
  • Refresh token: Also a JWT token whose sole purpose is to obtain a new pair of tokens from the server. A refresh token has a longer time span compared to the access token. Therefore, when an access token expires, the client can swiftly ask for a renewal by making a request to a designated refresh endpoint passing the refresh token in the Authentication header.

Each new pair of tokens replaces the existing ones.

Summary

In this post, we’ve taken a deep dive into the workings of JWT and highlighted its advantages like stateless authentication, enhanced security via signed tokens, and granular authorization based on claims. We’ve also demystified key jargon and discussed how to handle token expiration. Stayed tuned for our next post where we’ll tackle the implementation of JWT in Ktor. Thanks for joining us on this journey, and see you around!


Tomas Zezula

Hello! I'm a technology enthusiast with a knack for solving problems and a passion for making complex concepts accessible. My journey spans across software development, project management, and technical writing. I specialise in transforming rough sketches of ideas to fully launched products, all the while breaking down complex processes into understandable language. I believe a well-designed software development process is key to driving business growth. My focus as a leader and technical writer aims to bridge the tech-business divide, ensuring that intricate concepts are available and understandable to all. As a consultant, I'm eager to bring my versatile skills and extensive experience to help businesses navigate their software integration needs. Whether you're seeking bespoke software solutions, well-coordinated product launches, or easily digestible tech content, I'm here to make it happen. Ready to turn your vision into reality? Let's connect and explore the possibilities together.