Content is user-generated and unverified.

JWT Token Creation and Usage - Complete Guide

What is JWT?

JWT (JSON Web Token) is a compact, self-contained way to securely transmit information between parties as a JSON object.

JWT Structure:

A JWT token looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

It has three parts separated by dots (.):

HEADER.PAYLOAD.SIGNATURE
  1. Header (red): Algorithm and token type
json
   {
     "alg": "HS256",
     "typ": "JWT"
   }
  1. Payload (purple): User data and claims
json
   {
     "sub": "user123",
     "email": "user@example.com",
     "name": "John Doe",
     "iat": 1516239022,
     "exp": 1516325422
   }
  1. Signature (blue): Cryptographic signature to verify authenticity
   HMACSHA256(
     base64UrlEncode(header) + "." + base64UrlEncode(payload),
     secret
   )

Complete OAuth + JWT Flow in Your Application

Step-by-Step Process:

┌─────────────┐         ┌──────────────┐         ┌─────────────┐
│   Browser   │         │   Frontend   │         │   Backend   │
│             │         │    (React)   │         │ (Spring Boot)│
└──────┬──────┘         └──────┬───────┘         └──────┬──────┘
       │                       │                        │
       │  1. User clicks       │                        │
       │  "Sign in with Google"│                        │
       ├──────────────────────>│                        │
       │                       │                        │
       │                       │  2. Redirect to        │
       │                       │  accounts.google.com   │
       │<──────────────────────┤                        │
       │                       │                        │
┌──────▼──────┐               │                        │
│   Google    │               │                        │
│   Login     │               │                        │
└──────┬──────┘               │                        │
       │                       │                        │
       │  3. User logs in      │                        │
       │  at Google            │                        │
       │                       │                        │
       │  4. Google redirects  │                        │
       │  with authorization   │                        │
       │  code                 │                        │
       ├──────────────────────>│                        │
       │  /login/oauth2/code/  │                        │
       │  google?code=ABC123   │                        │
       │                       │                        │
       │                       │  5. Send code to       │
       │                       │  backend               │
       │                       ├───────────────────────>│
       │                       │  POST /auth/google     │
       │                       │  { "code": "ABC123" }  │
       │                       │                        │
       │                       │                        │ 6. Backend calls
       │                       │                        │ Google API to
       │                       │                        │ exchange code
       │                       │                        │ for access token
       │                       │                        ├────────┐
       │                       │                        │        │
       │                       │                        │<───────┘
       │                       │                        │
       │                       │                        │ 7. Get user info
       │                       │                        │ from Google
       │                       │                        ├────────┐
       │                       │                        │        │
       │                       │                        │<───────┘
       │                       │                        │
       │                       │                        │ 8. Create JWT
       │                       │                        │ token with
       │                       │                        │ user info
       │                       │                        ├────────┐
       │                       │                        │        │
       │                       │                        │<───────┘
       │                       │                        │
       │                       │  9. Return JWT token   │
       │                       │<───────────────────────┤
       │                       │  { "token": "eyJ..." } │
       │                       │                        │
       │                       │ 10. Store JWT in       │
       │                       │ localStorage           │
       │                       ├────────┐               │
       │                       │        │               │
       │                       │<───────┘               │
       │                       │                        │
       │  11. User is now      │                        │
       │  logged in            │                        │
       │<──────────────────────┤                        │
       │                       │                        │

Detailed Step-by-Step Explanation

Step 1-4: OAuth Flow (Getting Authorization Code)

What happens:

  1. User clicks "Sign in with Google" button
  2. Frontend redirects to Google's login page
  3. User authenticates with Google
  4. Google redirects back with an authorization code

URL after Google redirect:

https://taskmanager.sriinfosoft.com/login/oauth2/code/google?code=4/0AbCD1234567890xyz&state=random_state

Step 5: Frontend Sends Code to Backend

Frontend makes API call:

javascript
// React code
const handleGoogleCallback = async (code) => {
  try {
    const response = await fetch('https://api-taskmanager.sriinfosoft.com/auth/google', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ code: code })
    });
    
    const data = await response.json();
    const jwtToken = data.token;
    
    // Store JWT in localStorage
    localStorage.setItem('jwt_token', jwtToken);
    
    // Redirect to dashboard
    navigate('/dashboard');
  } catch (error) {
    console.error('Login failed:', error);
  }
};

Step 6-7: Backend Exchanges Code for Access Token

Spring Boot backend code:

java
@RestController
@RequestMapping("/auth")
public class AuthController {
    
    @Autowired
    private OAuth2AuthorizedClientService clientService;
    
    @Autowired
    private JwtTokenProvider jwtTokenProvider;
    
    @PostMapping("/google")
    public ResponseEntity<?> authenticateWithGoogle(@RequestBody AuthRequest request) {
        // 1. Exchange authorization code for access token
        OAuth2AccessToken accessToken = exchangeCodeForToken(request.getCode());
        
        // 2. Get user info from Google
        GoogleUserInfo userInfo = getUserInfoFromGoogle(accessToken);
        
        // 3. Save or update user in database
        User user = userService.createOrUpdateUser(userInfo);
        
        // 4. Generate JWT token
        String jwtToken = jwtTokenProvider.createToken(user);
        
        // 5. Return JWT to frontend
        return ResponseEntity.ok(new AuthResponse(jwtToken));
    }
    
    private GoogleUserInfo getUserInfoFromGoogle(OAuth2AccessToken accessToken) {
        // Make API call to Google
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(accessToken.getTokenValue());
        
        HttpEntity<String> entity = new HttpEntity<>(headers);
        
        ResponseEntity<GoogleUserInfo> response = restTemplate.exchange(
            "https://www.googleapis.com/oauth2/v2/userinfo",
            HttpMethod.GET,
            entity,
            GoogleUserInfo.class
        );
        
        return response.getBody();
    }
}

Google returns user info:

json
{
  "id": "1234567890",
  "email": "user@gmail.com",
  "verified_email": true,
  "name": "John Doe",
  "given_name": "John",
  "family_name": "Doe",
  "picture": "https://lh3.googleusercontent.com/a/...",
  "locale": "en"
}

Step 8: Creating the JWT Token

JwtTokenProvider class:

java
@Component
public class JwtTokenProvider {
    
    @Value("${jwt.secret}")
    private String jwtSecret;
    
    @Value("${jwt.expiration}")
    private long jwtExpiration;
    
    public String createToken(User user) {
        // Current time
        Date now = new Date();
        
        // Expiration time (24 hours from now)
        Date expiryDate = new Date(now.getTime() + jwtExpiration);
        
        // Build JWT token
        return Jwts.builder()
                .setSubject(user.getId().toString())           // User ID
                .claim("email", user.getEmail())                // User email
                .claim("name", user.getName())                  // User name
                .claim("roles", user.getRoles())                // User roles
                .setIssuedAt(now)                               // Issued time
                .setExpiration(expiryDate)                      // Expiration time
                .signWith(SignatureAlgorithm.HS256, jwtSecret) // Sign with secret
                .compact();
    }
}

Generated JWT token:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0IiwiZW1haWwiOiJ1c2VyQGdtYWlsLmNvbSIsIm5hbWUiOiJKb2huIERvZSIsInJvbGVzIjpbIlVTRVIiXSwiaWF0IjoxNzI5NDU2Nzg5LCJleHAiOjE3Mjk1NDMxODl9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Decoded payload:

json
{
  "sub": "1234",
  "email": "user@gmail.com",
  "name": "John Doe",
  "roles": ["USER"],
  "iat": 1729456789,
  "exp": 1729543189
}

Step 9-10: Frontend Stores JWT Token

Frontend receives JWT:

javascript
// Response from backend
{
  "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0...",
  "user": {
    "id": 1234,
    "email": "user@gmail.com",
    "name": "John Doe"
  }
}

Store in localStorage:

javascript
// Store JWT token
localStorage.setItem('jwt_token', response.token);

// Store user info (optional)
localStorage.setItem('user', JSON.stringify(response.user));

How JWT is Used After Login

Every API Request Includes JWT Token

Frontend includes JWT in Authorization header:

javascript
// React code - Making API calls with JWT
const getTasks = async () => {
  const token = localStorage.getItem('jwt_token');
  
  try {
    const response = await fetch('https://api-taskmanager.sriinfosoft.com/api/tasks', {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${token}`,  // ← JWT token included here
        'Content-Type': 'application/json',
      }
    });
    
    const tasks = await response.json();
    return tasks;
  } catch (error) {
    console.error('Failed to fetch tasks:', error);
  }
};

HTTP Request looks like:

http
GET /api/tasks HTTP/1.1
Host: api-taskmanager.sriinfosoft.com
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0...
Content-Type: application/json

Backend Validates JWT Token

JwtAuthenticationFilter intercepts every request:

java
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
    @Autowired
    private JwtTokenProvider jwtTokenProvider;
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        
        // 1. Extract JWT token from Authorization header
        String token = getJwtFromRequest(request);
        
        if (token != null && jwtTokenProvider.validateToken(token)) {
            // 2. Extract user ID from token
            Long userId = jwtTokenProvider.getUserIdFromToken(token);
            
            // 3. Load user details
            UserDetails userDetails = userDetailsService.loadUserById(userId);
            
            // 4. Create authentication object
            UsernamePasswordAuthenticationToken authentication = 
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            
            // 5. Set authentication in security context
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        
        // Continue with the request
        filterChain.doFilter(request, response);
    }
    
    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7); // Remove "Bearer " prefix
        }
        return null;
    }
}

JwtTokenProvider validates token:

java
public boolean validateToken(String token) {
    try {
        // Parse and verify JWT signature
        Jwts.parser()
            .setSigningKey(jwtSecret)
            .parseClaimsJws(token);
        return true;
    } catch (SignatureException ex) {
        // Invalid JWT signature
        return false;
    } catch (MalformedJwtException ex) {
        // Invalid JWT token
        return false;
    } catch (ExpiredJwtException ex) {
        // Expired JWT token
        return false;
    } catch (UnsupportedJwtException ex) {
        // Unsupported JWT token
        return false;
    } catch (IllegalArgumentException ex) {
        // JWT claims string is empty
        return false;
    }
}

public Long getUserIdFromToken(String token) {
    Claims claims = Jwts.parser()
        .setSigningKey(jwtSecret)
        .parseClaimsJws(token)
        .getBody();
    
    return Long.parseLong(claims.getSubject());
}

JWT Token Lifecycle

┌──────────────────────────────────────────────────────────┐
│                    JWT Token Lifecycle                   │
└──────────────────────────────────────────────────────────┘

1. CREATION (Backend)
   ├─ User logs in successfully
   ├─ Backend generates JWT with user info
   ├─ JWT signed with secret key
   └─ JWT sent to frontend

2. STORAGE (Frontend)
   ├─ JWT stored in localStorage or sessionStorage
   ├─ User info extracted and stored separately (optional)
   └─ Token persists across page refreshes

3. USAGE (Every API Request)
   ├─ Frontend includes JWT in Authorization header
   ├─ Format: "Authorization: Bearer <token>"
   └─ Sent with EVERY authenticated API request

4. VALIDATION (Backend - Every Request)
   ├─ Backend extracts token from Authorization header
   ├─ Verifies signature using secret key
   ├─ Checks expiration time
   ├─ Extracts user info from payload
   └─ Allows or denies request

5. EXPIRATION (After 24 hours)
   ├─ Token becomes invalid
   ├─ Backend rejects requests with expired token
   ├─ Frontend receives 401 Unauthorized
   └─ User must log in again

6. REFRESH (Optional)
   ├─ Use refresh token to get new access token
   ├─ Or simply require user to log in again
   └─ New JWT issued with new expiration

Security Features of JWT

1. Stateless Authentication

  • Server doesn't need to store session data
  • JWT contains all necessary information
  • Scales well (no shared session storage needed)

2. Cryptographically Signed

  • Signature prevents tampering
  • If payload is modified, signature verification fails
  • Only server with secret key can create valid tokens

3. Expiration Time

  • Built-in expiration (exp claim)
  • Limits damage if token is stolen
  • Forces periodic re-authentication

4. Cannot Be Modified by Client

  • Client can read the payload (it's just base64-encoded)
  • But any modification breaks the signature
  • Server will reject modified tokens

JWT vs Session Cookies

FeatureJWTSession Cookie
StorageFrontend (localStorage)Server-side
StatelessYesNo (requires session store)
Cross-domainEasy (just send token)Complex (CORS, SameSite)
SizeLarger (contains user info)Smaller (just session ID)
RevocationHard (need blacklist)Easy (delete session)
ScalingEasy (no shared state)Complex (shared session store)

Complete Example: Frontend React Code

javascript
// Auth context provider
import { createContext, useContext, useState, useEffect } from 'react';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [token, setToken] = useState(localStorage.getItem('jwt_token'));

  // Check if user is logged in on app load
  useEffect(() => {
    if (token) {
      // Decode JWT to get user info (without verification - that's done on backend)
      const payload = JSON.parse(atob(token.split('.')[1]));
      setUser({
        id: payload.sub,
        email: payload.email,
        name: payload.name,
      });
    }
  }, [token]);

  const login = async (code) => {
    const response = await fetch('https://api-taskmanager.sriinfosoft.com/auth/google', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ code }),
    });
    
    const data = await response.json();
    
    // Store token
    localStorage.setItem('jwt_token', data.token);
    setToken(data.token);
    setUser(data.user);
  };

  const logout = () => {
    localStorage.removeItem('jwt_token');
    setToken(null);
    setUser(null);
  };

  // API call with authentication
  const apiCall = async (url, options = {}) => {
    const headers = {
      'Content-Type': 'application/json',
      ...options.headers,
    };

    if (token) {
      headers['Authorization'] = `Bearer ${token}`;
    }

    const response = await fetch(url, {
      ...options,
      headers,
    });

    if (response.status === 401) {
      // Token expired or invalid
      logout();
      throw new Error('Session expired');
    }

    return response;
  };

  return (
    <AuthContext.Provider value={{ user, login, logout, apiCall }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

Using the auth context:

javascript
// In a React component
import { useAuth } from './AuthContext';

function Dashboard() {
  const { user, apiCall } = useAuth();
  const [tasks, setTasks] = useState([]);

  useEffect(() => {
    const fetchTasks = async () => {
      try {
        const response = await apiCall('https://api-taskmanager.sriinfosoft.com/api/tasks');
        const data = await response.json();
        setTasks(data);
      } catch (error) {
        console.error('Failed to fetch tasks:', error);
      }
    };

    fetchTasks();
  }, []);

  return (
    <div>
      <h1>Welcome, {user.name}!</h1>
      <ul>
        {tasks.map(task => (
          <li key={task.id}>{task.title}</li>
        ))}
      </ul>
    </div>
  );
}

Summary

JWT Token Creation:

  1. User authenticates with Google OAuth
  2. Backend receives authorization code
  3. Backend exchanges code for user info
  4. Backend creates JWT with user info and signs it with secret key
  5. JWT sent to frontend

JWT Token Usage:

  1. Frontend stores JWT in localStorage
  2. Every API request includes JWT in Authorization header
  3. Backend validates JWT signature and expiration
  4. Backend extracts user info from JWT
  5. Backend allows or denies request based on token validity

Key Benefits:

  • ✅ Stateless (no server-side sessions)
  • ✅ Works great with microservices and separate domains
  • ✅ Contains all user info (no database lookup needed)
  • ✅ Cryptographically secure
  • ✅ Built-in expiration

This is why JWT is perfect for your React + Spring Boot separated architecture with CloudFront!

Content is user-generated and unverified.
    JWT Token Creation and Usage - Complete Guide with OAuth Flow | Claude