Spring Boot - OAuth2 with JWT

Last Updated : 28 Oct, 2025

OAuth2 with JWT (JSON Web Token) is a widely used authentication mechanism in modern Spring Boot applications. OAuth2 provides secure authorization for APIs, while JWT enables stateless token-based authentication between client and server. Together, they form a robust and scalable security solution.

OAuth2

OAuth2 (Open Authorization 2.0) is an authorization framework that enables applications to access user resources on another service securely without sharing credentials.

It issues access tokens to clients after successful authentication.

Key Roles:

  • Resource Owner: The user granting access.
  • Client: The application requesting access.
  • Authorization Server: Authenticates the user and issues tokens.
  • Resource Server: Hosts the protected resources and validates access tokens.

JWT

JWT (JSON Web Token) is a compact and stateless token format used for securely transmitting information between parties.

It contains three parts: Header, Payload, and Signature.

Structure:

Header.Payload.Signature

Key Properties:

  • Encoded in Base64
  • Self-contained (stores user roles, expiration, etc.)
  • Signed using a secret key or private key

How OAuth2 Works with JWT in Spring Boot

When combined, OAuth2 and JWT handle both authentication and authorization:

  1. The user logs in with valid credentials.
  2. The Authorization Server validates them and generates a JWT.
  3. The client stores the JWT.
  4. For each request, the JWT is sent in the HTTP Authorization header.
  5. The Resource Server validates the token before granting access.

Example Project:

In this project, we will develop the register API, login API's, and token generator.

  • Once the user registers, username and password details are stored in the MongoDB database.
  • Passwords can store encoded forms, and at the same time, they can generate JWT.
  • Hence, users can login and access the database, but they need to have user credentials along with a JWT token.
  • Once the credentials and tokens are matched, they can log into the database.
Auth with JWT

Project Implementation

Step 1. Create Project

Create a new Spring Boot project using Spring Initializr:

  • Project: Maven
  • Language: Java
  • Spring Boot Version: 3.3.4

Project Structure:

fileStructure
Project Structure

Step 2. Add dependencies

Add the following dependencies in the pom.xml file:

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-security</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.security</groupId>

<artifactId>spring-security-oauth2-authorization-server</artifactId>

<version>1.3.0</version>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

<dependency>

<groupId>com.h2database</groupId>

<artifactId>h2</artifactId>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<optional>true</optional>

</dependency>

</dependencies>

Step 3. Configure application.properties

spring.application.name=spring-oauth2-jwt-demo


# H2 Database

spring.datasource.url=jdbc:h2:mem:testdb

spring.datasource.driver-class-name=org.h2.Driver

spring.datasource.username=sa

spring.datasource.password=

spring.jpa.hibernate.ddl-auto=create-drop

spring.h2.console.enabled=true


# JWT Configuration

jwt.secret=MyJwtSecretKey12345

jwt.expiration=3600000

server.port=8080

Step 4. Entity Class

Create a User entity to store credentials.

Java
package com.gfg.oauth2jwt.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String password;
    private String role;
}

Step 5. Repository Layer

Java
package com.gfg.oauth2jwt.repository;

import com.gfg.oauth2jwt.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}

Step 6. Service Layer

Create a service implementing UserDetailsService for authentication.

Java
package com.gfg.oauth2jwt.service;

import com.gfg.oauth2jwt.entity.User;
import com.gfg.oauth2jwt.repository.UserRepository;
import org.springframework.security.core.userdetails.*;
import org.springframework.stereotype.Service;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final UserRepository repo;

    public CustomUserDetailsService(UserRepository repo) {
        this.repo = repo;
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        User user = repo.findByUsername(username);
        if (user == null) throw new UsernameNotFoundException("User not found");
        return org.springframework.security.core.userdetails.User
                .withUsername(user.getUsername())
                .password("{noop}" + user.getPassword())
                .roles(user.getRole())
                .build();
    }
}

Step 7. JWT Utility Class

Create a utility for token generation and validation.

Java
package com.gfg.oauth2jwt.util;

import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.*;

@Component
public class JwtUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private long expiration;

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expiration))
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    public String extractUsername(String token) {
        return Jwts.parser().setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody().getSubject();
    }

    public boolean validateToken(String token, String username) {
        return username.equals(extractUsername(token)) && !isTokenExpired(token);
    }

    private boolean isTokenExpired(String token) {
        return Jwts.parser().setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody().getExpiration().before(new Date());
    }
}

Step 8. Security Configuration

Use Spring Boot 3.x style SecurityFilterChain configuration.

Java
package com.gfg.oauth2jwt.config;

import com.gfg.oauth2jwt.service.CustomUserDetailsService;
import org.springframework.context.annotation.*;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final CustomUserDetailsService userDetailsService;

    public SecurityConfig(CustomUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
        return http.getSharedObject(AuthenticationManagerBuilder.class)
                .userDetailsService(userDetailsService)
                .and()
                .build();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http.csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/auth/login").permitAll()
                        .anyRequest().authenticated())
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .build();
    }
}

Step 9. Controller Layer

Expose a login endpoint to generate tokens and a secured API.

Java
package com.gfg.oauth2jwt.controller;

import com.gfg.oauth2jwt.service.CustomUserDetailsService;
import com.gfg.oauth2jwt.util.JwtUtil;
import org.springframework.security.authentication.*;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/auth")
public class AuthController {

    private final AuthenticationManager authenticationManager;
    private final CustomUserDetailsService userDetailsService;
    private final JwtUtil jwtUtil;

    public AuthController(AuthenticationManager authenticationManager,
                          CustomUserDetailsService userDetailsService,
                          JwtUtil jwtUtil) {
        this.authenticationManager = authenticationManager;
        this.userDetailsService = userDetailsService;
        this.jwtUtil = jwtUtil;
    }

    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password) {
        authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        UserDetails user = userDetailsService.loadUserByUsername(username);
        return jwtUtil.generateToken(user.getUsername());
    }

    @GetMapping("/secure")
    public String secureEndpoint() {
        return "Access granted to secured resource";
    }
}

Step 10. Run and Test

  • Run the application.
  • Access H2 console at http://localhost:8080/h2-console.
  • Insert a test user:

INSERT INTO USER (username, password, role) VALUES ('user1', 'password', 'USER');

  • Generate a token:

POST /auth/login?username=user1&password=password

Response: JWT token string

  • Access secured endpoint:

GET /api/auth/login

Header: Authorization: Bearer <token>

Token added int AuthorizationLogin details are added in the body in json format
Comment

Explore