Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding JWT Tokens: Structure, Security, and Implementation

Tech 2

The Problem with Traditional Tokens

When a client obtains a token from an authentication server and then uses that token to access protected resources, the resource server must verify the token's validity.

The verification flow typically works as follows:

  1. The client presents the token when requesting resources
  2. The resource server makes a remote call to the authentication service to verify the token
  3. If valid, the resource server returns the requested data

This approach has a significant drawback: every token verification requires a network call to the authentication service, which impacts performance. A better solution is to let the resource server verify tokens locally without remote calls.

JWT (JSON Web Token) addresses this issue. When users authenticate successfully, they receive a JWT containing all necessary user information. Clients can then access resource servers directly with this token, and the resource server validates it using a predefined algorithm—no authentication service call required.

What is JWT

JSON Web Token is an open standard (RFC 7519) that defines a compact, self-contained format for transmitting JSON objects between parties. Information within JWTs is digitally signed, allowing recipients to verify authenticity and detect tampering. JWTs can use HMAC algorithms or RSA public/private key pairs for signature verification. The official specification is available at jwt.io.

JWT enables stateless authentication. Traditional session-based approaches store user identity information on the server, creating storage overhead and poor scalability in distributed systems. Session replication or sticky sessions become necessary to handle requests across multiple application servers.

Stateless authentication with JWTs solves these problems. The server doesn't need to store sessions—user identity information lives within the token itself. After successful authentication, the user receives a token, stores it client-side, and includes it with each request. Resource servers extract user information directly from the JWT.

Advantages of JWT:

  • JSON format makes parsing straightforward
  • Customizable payload allows flexible content expansion
  • Digital signatures using asymmetric encryption prevent tampering
  • Resource servers can authorize requests independently without calling authentication services

Disadvantages:

  • Tokens can be lengthy, consuming more storage space

JWT Structure

A JWT consists of three parts separated by dots: xxxxx.yyyyy.zzzzz

Header

The header specifies the token type (JWT) and the signing algorithm (such as HMAC SHA256 or RSA).

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

The header JSON is Base64Url encoded to produce the first segmant of the token.

Payload

The second segment contains the payload—a JSON object holding the claims. JWT defines standard claims including iss (issuer), exp (expiration time), and sub (subject). Custom claims can also be added.

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

The payload is Base64Url encoded to form the second segment. Avoid storing sensitive information here since the payload is encoded but not encrypted.

Signature

The third segment prevents content tampering. It's created by Base64Url encoding the header and payload, concatenating them with a dot, then generating a signature using the algorithm specified in the header.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

The signature ensures that if an attacker modifies the token content, validation fails.

Why JWT Prevents Tampering

The signature is generated using a cryptographic algorithm that requires a secret key. If anyone changes the header or payload, the signature becomes invalid because verification requires the same content and key. This makes JWTs tamper-resistant.

Two encryption approaches exist:

Symmetric encryption uses a shared secret key between the authentication service and resource servers. It's fast but vulnerable if the key is compromised.

Asymmetric encryption keeps the private key only on the authantication service while distributing the public key to clients and resource services. Since public and private keys are paired, this approach is more secure despite slower performance.

Configuration Example

package com.example.auth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import java.util.Arrays;

@Configuration
public class TokenConfiguration {

    private static final String SECRET_KEY = "secret123";

    @Autowired
    TokenStore tokenStore;

    @Autowired
    private JwtAccessTokenConverter tokenConverter;

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(tokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter tokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SECRET_KEY);
        return converter;
    }

    @Bean(name = "customTokenServices")
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices services = new DefaultTokenServices();
        services.setSupportRefreshToken(true);
        services.setTokenStore(tokenStore);

        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        enhancerChain.setTokenEnhancers(Arrays.asList(tokenConverter));
        services.setTokenEnhancer(enhancerChain);

        services.setAccessTokenValiditySeconds(7200);
        services.setRefreshTokenValiditySeconds(259200);
        return services;
    }
}

Sample Token Response

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expires_in": 7198,
    "scope": "all",
    "jti": "dddfe91d-bbe2-4a08-be24-48fc457f0217"
}

Field explanations:

  • access_token: The JWT used for accessing protected resources
  • token_type: Bearer type per RFC 6750; include this before the token in the Authorization header
  • refresh_token: Used to obtain a new access token before expiration
  • expires_in: Token lifetime in seconds
  • scope: Permission scope granted to the token
  • jti: Unique token identifier

Embedding Custom Claims

After validating user credentials, you can embed additional user information into the JWT by serializing it as a JSON string and using it as the username claim:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    UserAccount account = userRepository
        .findByUsername(username);
    
    if (account == null) {
        return null;
    }
    
    String userPassword = account.getPassword();
    String[] permissions = {"read", "write"};
    
    account.setPassword(null);
    String userJson = JSON.toJSONString(account);
    
    return User.withUsername(userJson)
        .password(userPassword)
        .authorities(permissions)
        .build();
}

This approach embeds complete user data within the token, avoiding the need for additional database calls when processing requests.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.