Open In App

Spring Boot – OAuth2 with JWT

Last Updated : 14 Feb, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

OAuth 2.0 is defined as Open Authorization (Version 2.0), and it is a widely used authorization framework that can be used by third-party applications to gain limited access to a user’s HTTP service, which means allowing the specified user to allow the third-party application to obtain access on its own.

Implementation of OAuth with JWT (JSON Web Tokens)

  • Client: The application that is attempting to access the user’s data. It could be a web or mobile application.
  • Resource Owner: The user who owns the data that the client is trying to access. The resource owner grants permission to the client to access the data.
  • Authorization Server: The authorization server that authenticates the resource owner and issues access tokens after getting proper authorization.
  • Resource Server: The resource server that hosts the protected resources. It validates the access token and serves the requested resource if the token is valid.
  • Authorization Grant: The Authorization Grant is the credential representing the resource owner’s authorization (password, client credentials, Authorization code)
  • Token: The access token represents authorization permission for the client.
  • JWT: JWT is defined as a JSON Web Token that can be URL-safe and represents claims to be transferred between two parties. JWT can be used as an access token in OAuth 2.0.
  • JWT Claims: The pieces of information that are conveyed in a JWT. They can be things like user identity, user roles, expiration time, etc.
  • Scope: Define the permissions that the client is requesting. For example, read-only access or full 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.

Design Diagram:

Auth with JWT

Prerequisites:

  • Project: Maven
  • Language: Java
  • Packaging: Jar
  • Java: 17

Developing OAuth with JWT Access API’s

Step 1: Create a new Spring Boot project in the Spring STS IDE application and open it. For this project, choose the following things:

Please add the following dependencies while creating the project:

  • Spring DevTools
  • Spring Data MongoDB
  • Spring Web
  • Lombok
  • OAuth Resource Server

Once you click on Finish the project, open it in the Spring STS IDE application.

Project Structure

Step 2: Connect to a MongoDB local host with a MongoDB connection URL and add it to the application.properities file renamed into application.yml file and add the below properties.

define the access token paths, both private and public
access-token:
private: ${ACCESS_TOKEN_PRIVATE_KEY_PATH}
public: ${ACCESS_TOKEN_PUBLIC_KEY_PATH}
// define the refresh token paths
refresh-token:
private: ${REFRESH_TOKEN_PRIVATE_KEY_PATH}
public: ${REFRESH_TOKEN_PUBLIC_KEY_PATH}
spring:
profiles:
active: dev
Connect to the MongoDB database
data:
mongodb:
uri: mongodb://${MONGODB_HOST}:${MONGODB_PORT}/?authSource=${MONGODB_DB}
database: ${MONGODB_DB}
logging:
level:
org.springframework.security: DEBUG


Once we establish the connection with MongoDB and add the MongoDB URL, once the program is run, the database will be in MongoDB local with the given names in the application-dev.yml. This can be created in the resource folder of the project and add the all-environment values of the project. Here the values are shown below in the file name as application-dev.yml.

In YAML, the structure is based on indentation, and key-value pairs are represented using the key-value syntax. If you need to convert these properties to JSON or another format, you can use a similar key-value pair representation.

ACCESS_TOKEN_PRIVATE_KEY_PATH: "access-refresh-token-keys/access-token-private.key"
ACCESS_TOKEN_PUBLIC_KEY_PATH: "access-refresh-token-keys/access-token-public.key"
REFRESH_TOKEN_PRIVATE_KEY_PATH: "access-refresh-token-keys/refresh-token-private.key"
REFRESH_TOKEN_PUBLIC_KEY_PATH: "access-refresh-token-keys/refresh-token-public.key"
MONGODB_HOST: "localhost"
MONGODB_PORT: '27017'
MONGODB_DB: "db1"


You can also refer to the below images:

application.yml file

This application-dev.yml file contains the proper values of the project :

application-dev.yml file

Step 3: Create the document user class in the package of the user document.

Go to the src > main > java > userDocument and create a User class and put the below code.

Java




package com.example.oAuth.userDocument;
  
import java.util.Collection;
import java.util.Collections;
  
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
  
import com.mongodb.lang.NonNull;
  
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
  
@Document
@Data
@RequiredArgsConstructor
@NoArgsConstructor
public class User implements UserDetails {
  
    @Id
    private String id;
  
    @NonNull
    private String userName;
  
    @NonNull
    private String password;
      
      
  
    // Other fields or methods can be added as needed
    public User() {
          
    }
    public User(String id, String userName, String password) {
        super();
        this.id = id;
        this.userName = userName;
        this.password = password;
    }
    public User( String userName, String password) {
        super();
          
        this.userName = userName;
        this.password = password;
    }
  
  
    public String getUserName() {
        return userName;
    }
  
      
    public void setUserName(String userName) {
        this.userName = userName;
    }
  
    public String getPassword() {
        return password;
    }
  
    public void setPassword(String password) {
        this.password = password;
    }
  
    public String getId() {
        return id;
    }
    public void setId(String id) {
        // TODO Auto-generated method stub
        this.id = id;
          
    }
  
    // Methods required by Spring Security for user details
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Collections.emptyList();
    }
  
    public boolean isAccountNonExpired() {
        return true;
    }
  
    public boolean isAccountNonLocked() {
        return true;
    }
  
    public boolean isCredentialsNonExpired() {
        return true;
    }
  
    public boolean isEnabled() {
        return true;
    }
    @Override
    public String getUsername() {
        // TODO Auto-generated method stub
        return null;
    }
      
  
      
}


Step 4: Create the user repository and add the code below:

Go to the src > main > java > oAuth > repository and create an interface UserRepository and put the below code.

Java




package com.example.oAuth.repository;
  
import java.util.Optional;
  
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
  
import com.example.oAuth.userDocument.User;
  
@Repository
public interface UserRepository extends MongoRepository<User, String>{
    boolean existsByUserName(String username);
    Optional<User> findByUserName(String username);
  
}


Step 5: Create one more package for the web security configuration named as securityOAuthConfig and create the class for web security and that class name as WebSecurity after creating the class put the below code:

Go to the src > main > java > securityOAuthConfig and create a WebSecurity class and put the below code.

Java




package com.example.oAuth.securityOAuthConfig;
  
  
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import lombok.extern.slf4j.Slf4j;
import org.bson.json.Converter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import static org.springframework.security.config.http.SessionCreationPolicy.*;
import static org.springframework.security.config.annotation.web.builders.HttpSecurity.*;
  
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableMethodSecurity
@Slf4j
public class WebSecurity {
    @Autowired
    JWTtoUserConvertor jwtToUserConverter;
    @Autowired
    KeyUtils keyUtils;
    @Autowired
    PasswordEncoder passwordEncoder;
    @Autowired
    UserDetailsManager userDetailsManager;
  
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((authorize) -> authorize
                                .requestMatchers("/api/auth/*").permitAll()
                                .anyRequest().authenticated()
                )
                .csrf(csrf -> csrf.disable())
                .cors(cors -> cors.disable())
                .httpBasic(basic -> basic.disable())
                .oauth2ResourceServer((oauth2) ->
                        oauth2.jwt((jwt) -> jwt.jwtAuthenticationConverter(jwtToUserConverter))
                )
                .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling((exceptions) -> exceptions
                                .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                                .accessDeniedHandler(new BearerTokenAccessDeniedHandler())
                );
        return http.build();
    }
  
    @Bean
    @Primary
    JwtDecoder jwtAccessTokenDecoder() {
        return NimbusJwtDecoder.withPublicKey(keyUtils.getAccessTokenPublicKey()).build();
    }
  
    @Bean
    @Primary
    JwtEncoder jwtAccessTokenEncoder() {
        JWK jwk = new RSAKey
                .Builder(keyUtils.getAccessTokenPublicKey())
                .privateKey(keyUtils.getAccessTokenPrivateKey())
                .build();
        JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
        return new NimbusJwtEncoder(jwks);
    }
  
    @Bean
    @Qualifier("jwtRefreshTokenDecoder")
    JwtDecoder jwtRefreshTokenDecoder() {
        return NimbusJwtDecoder.withPublicKey(keyUtils.getRefreshTokenPublicKey()).build();
    }
  
    @Bean
    @Qualifier("jwtRefreshTokenEncoder")
    JwtEncoder jwtRefreshTokenEncoder() {
        JWK jwk = new RSAKey
                .Builder(keyUtils.getRefreshTokenPublicKey())
                .privateKey(keyUtils.getRefreshTokenPrivateKey())
                .build();
        JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
        return new NimbusJwtEncoder(jwks);
    }
  
    @Bean
    @Qualifier("jwtRefreshTokenAuthProvider")
    JwtAuthenticationProvider jwtRefreshTokenAuthProvider() {
        JwtAuthenticationProvider provider = new JwtAuthenticationProvider(jwtRefreshTokenDecoder());
        provider.setJwtAuthenticationConverter(jwtToUserConverter);
        return provider;
    }
  
    @Bean
    DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setPasswordEncoder(passwordEncoder);
        provider.setUserDetailsService(userDetailsManager);
        return provider;
    }
}


Step 6: Create the one more class for the token generator for the web authorization and it named as TokenGenerator.

Go to the src > main > java > securityOAuthConfig > and create a TokenGenerator class and put the below code.

Java




package com.example.oAuth.securityOAuthConfig;
  
import java.text.MessageFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import com.example.oAuth.userDocument.User;
import com.example.oAuth.userModel.Token;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.stereotype.Component;
  
@Component
public class TokenGenerator {
      
    @Autowired
    JwtEncoder accessTokenEncoder;
  
    @Autowired
    @Qualifier("jwtRefreshTokenEncoder")
    JwtEncoder refreshTokenEncoder;
      
    private String createAccessToken(Authentication authentication) {
        User user = (User) authentication.getPrincipal();
        Instant now = Instant.now();
  
        JwtClaimsSet claimsSet = JwtClaimsSet.builder()
                .issuer("myApp")
                .issuedAt(now)
                .expiresAt(now.plus(5, ChronoUnit.MINUTES))
                .subject(user.getId())
                .build();
  
        return accessTokenEncoder.encode(JwtEncoderParameters.from(claimsSet)).getTokenValue();
    }
      
    private String createRefreshToken(Authentication authentication) {
        User user = (User) authentication.getPrincipal();
        Instant now = Instant.now();
  
        JwtClaimsSet claimsSet = JwtClaimsSet.builder()
                .issuer("myApp")
                .issuedAt(now)
                .expiresAt(now.plus(30, ChronoUnit.DAYS))
                .subject(user.getId())
                .build();
  
        return refreshTokenEncoder.encode(JwtEncoderParameters.from(claimsSet)).getTokenValue();
    }
    public Token createToken(Authentication authentication) {
        if (!(authentication.getPrincipal() instanceof User user)) {
            throw new BadCredentialsException(
                    MessageFormat.format("principal {0} is not of User type", authentication.getPrincipal().getClass())
            );
        }
  
        Token tokenDTO = new Token();
        tokenDTO.setUserId(user.getId());
        tokenDTO.setAccessToken(createAccessToken(authentication));
  
        String refreshToken;
        if (authentication.getCredentials() instanceof Jwt jwt) {
            Instant now = Instant.now();
            Instant expiresAt = jwt.getExpiresAt();
            Duration duration = Duration.between(now, expiresAt);
            long daysUntilExpired = duration.toDays();
            if (daysUntilExpired < 7) {
                refreshToken = createRefreshToken(authentication);
            } else {
                refreshToken = jwt.getTokenValue();
            }
        } else {
            refreshToken = createRefreshToken(authentication);
        }
        tokenDTO.setRefreshToken(refreshToken);
  
        return tokenDTO;
    }
  
}


Step 7: Create the class for the token configuration for the web authorization and it named as KeyUtils.

Go to the src > main > java > securityOAuthConfig > and create a KeyUtils class and put the below code.

Java




package com.example.oAuth.securityOAuthConfig;
import lombok.extern.slf4j.Slf4j;
  
import org.apache.juli.logging.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
  
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Objects;
  
@Component
@Slf4j
public class KeyUtils {
    @Autowired
    Environment environment;
  
    @Value("${access-token.private}")
    private String accessTokenPrivateKeyPath;
  
    @Value("${access-token.public}")
    private String accessTokenPublicKeyPath;
  
    @Value("${refresh-token.private}")
    private String refreshTokenPrivateKeyPath;
  
    @Value("${refresh-token.public}")
    private String refreshTokenPublicKeyPath;
  
    private KeyPair _accessTokenKeyPair;
    private KeyPair _refreshTokenKeyPair;
  
    private KeyPair getAccessTokenKeyPair() {
        if (Objects.isNull(_accessTokenKeyPair)) {
            _accessTokenKeyPair = getKeyPair(accessTokenPublicKeyPath, accessTokenPrivateKeyPath);
        }
        return _accessTokenKeyPair;
    }
  
    private KeyPair getRefreshTokenKeyPair() {
        if (Objects.isNull(_refreshTokenKeyPair)) {
            _refreshTokenKeyPair = getKeyPair(refreshTokenPublicKeyPath, refreshTokenPrivateKeyPath);
        }
        return _refreshTokenKeyPair;
    }
  
    private KeyPair getKeyPair(String publicKeyPath, String privateKeyPath) {
        KeyPair keyPair;
  
        File publicKeyFile = new File(publicKeyPath);
        File privateKeyFile = new File(privateKeyPath);
  
        if (publicKeyFile.exists() && privateKeyFile.exists()) {
            //log.info("loading keys from file: {}, {}", publicKeyPath, privateKeyPath);
            try {
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
  
                byte[] publicKeyBytes = Files.readAllBytes(publicKeyFile.toPath());
                EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);
                PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
  
                byte[] privateKeyBytes = Files.readAllBytes(privateKeyFile.toPath());
                PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
                PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
  
                keyPair = new KeyPair(publicKey, privateKey);
                return keyPair;
            } catch (NoSuchAlgorithmException | IOException | InvalidKeySpecException e) {
                throw new RuntimeException(e);
            }
        } else {
            if (Arrays.stream(environment.getActiveProfiles()).anyMatch(s -> s.equals("prod"))) {
                throw new RuntimeException("public and private keys don't exist");
            }
        }
  
        File directory = new File("access-refresh-token-keys");
        if (!directory.exists()) {
            directory.mkdirs();
        }
        try {
            //log.info("Generating new public and private keys: {}, {}", publicKeyPath, privateKeyPath);
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
            try (FileOutputStream fos = new FileOutputStream(publicKeyPath)) {
                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyPair.getPublic().getEncoded());
                fos.write(keySpec.getEncoded());
            }
  
            try (FileOutputStream fos = new FileOutputStream(privateKeyPath)) {
                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded());
                fos.write(keySpec.getEncoded());
            }
        } catch (NoSuchAlgorithmException | IOException e) {
            throw new RuntimeException(e);
        }
  
        return keyPair;
    }
  
  
    public RSAPublicKey getAccessTokenPublicKey() {
        return (RSAPublicKey) getAccessTokenKeyPair().getPublic();
    };
    public RSAPrivateKey getAccessTokenPrivateKey() {
        return (RSAPrivateKey) getAccessTokenKeyPair().getPrivate();
    };
    public RSAPublicKey getRefreshTokenPublicKey() {
        return (RSAPublicKey) getRefreshTokenKeyPair().getPublic();
    };
    public RSAPrivateKey getRefreshTokenPrivateKey() {
        return (RSAPrivateKey) getRefreshTokenKeyPair().getPrivate();
    };
}


Step 8: Create the class for the JWT (JSON Web Token) configuration for the user authorization and it named as JWTtoUserConvertor.

Go to the src > main > java > securityOAuthConfig > and create a JWTtoUserConvertor class and put the below code.

Java




package com.example.oAuth.securityOAuthConfig;
  
import java.util.Collections;
  
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.stereotype.Component;
import com.example.oAuth.userDocument.User;
  
  
  
@Component
public class JWTtoUserConvertor implements Converter<Jwt, UsernamePasswordAuthenticationToken> {
  
    @Override
    public UsernamePasswordAuthenticationToken convert(Jwt source) {
         User user = new User();
            user.setId(source.getSubject());
            return new UsernamePasswordAuthenticationToken(user, source, Collections.EMPTY_LIST);
    }
      
  
}


Step 9: Create the package and named as userModel . Create the class for UserDTO for user configuration for the JWT (JSON Web Token) configuration for the user authorization and it named as JWTtoUserConvertor.

Go to the src > main > java > userModel > and create a UserDTO class and put the below code.

Java




package com.example.oAuth.userModel;
  
import com.example.oAuth.userDocument.User;
  
import lombok.Builder;
import lombok.Data;
  
@Builder
@Data
public class UserDTO {
    private String id;
    private String username;
  
    public static UserDTO from(User user) {
        return UserDTO.builder()  // Corrected the reference to the builder
                .id(user.getId())
                .username(user.getUserName())
                .build();
    }
}


Step 10: Create the class SignUp for user registration.

Go to the src > main > java > userModel > and create a SignUp class and put the below code.

Java




package com.example.oAuth.userModel;
  
import lombok.Getter;
import lombok.Setter;
  
@Getter
@Setter
public class SignUp {
      
    private String userName;
    private String password;
      
    public SignUp() {
          
    }
      
      
    public SignUp(String userName, String password) {
        super();
        this.userName = userName;
        this.password = password;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
      
      
  
}


Step 11: Create the class Login for user login.

Go to the src > main > java > userModel > and create a Login class and put the below code.

Java




package com.example.oAuth.userModel;
  
public class Login {
      
    private String userName;
    private String password;
      
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
      
      
  
}


Step 12: Create the class Token for user validation through the web authorization to accesses the data from the database.

Go to the src > main > java > userModel > and create a Token class and put the below code.

Java




package com.example.oAuth.userModel;
  
public class Token {
      
    private String userId;
    private String accessToken;
    private String refreshToken;
    public String getUserId() {
        return userId;
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    public String getAccessToken() {
        return accessToken;
    }
    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }
    public String getRefreshToken() {
        return refreshToken;
    }
    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }
      
      
  
}


Step 13: Create one more package named as Service and class UserManager for user validation through the web authorization.

Go to the src > main > java > Service > and create a UserManager class and put the below code.

Java




package com.example.oAuth.service;
  
import java.text.MessageFormat;
import java.util.Optional;
  
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;
  
import com.example.oAuth.repository.UserRepository;
import com.example.oAuth.userDocument.User;
  
@Service
public class UserManager implements UserDetailsManager {
  
    @Autowired
    UserRepository userRepository;
  
    @Autowired
    PasswordEncoder passwordEncoder;
  
    @Override
    public void createUser(UserDetails user) {
         
        ((User) user).setPassword(passwordEncoder.encode(user.getPassword()));
        // Save the user in the repository
        userRepository.save((User) user);
    }
  
    @Override
    public void updateUser(UserDetails user) {
        // You can implement this method to update user details if needed
        // For example, updating roles or other user information
    }
  
    @Override
    public void deleteUser(String username) {
        // You can implement this method to delete a user by username
        // Typically used when a user wants to delete their account
    }
  
    @Override
    public void changePassword(String oldPassword, String newPassword) {
        // You can implement this method to change the user's password
        // For example, when a user wants to change their password
    }
  
    @Override
    public boolean userExists(String username) {
          
        return false;
    }
  
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // Fetch the user from the repository by username
        Optional<User> userOptional = userRepository.findByUserName(username);
  
        // Check if the user exists
        if (userOptional.isEmpty()) {
            throw new UsernameNotFoundException(MessageFormat.format("User with username {0} not found", username));
        }
  
        // Return the UserDetails extracted from the User entity
        return userOptional.get();
    }
  
}


Step 14: Create one more package named as Controller and class AuthController for user validation through the web JWT Token authorization.

Go to the src > main > java > Controller> and create a AuthController class and put the below code.

Java




package com.example.oAuth.controller;
  
import com.example.oAuth.securityOAuthConfig.TokenGenerator;
import com.example.oAuth.userDocument.User;
import com.example.oAuth.userModel.Login;
import com.example.oAuth.userModel.SignUp;
import com.example.oAuth.userModel.Token;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
  
import java.util.Collections;
  
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @Autowired
    UserDetailsManager userDetailsManager;
    @Autowired
    TokenGenerator tokenGenerator;
    @Autowired
    DaoAuthenticationProvider daoAuthenticationProvider;
    @Autowired
    @Qualifier("jwtRefreshTokenAuthProvider")
    JwtAuthenticationProvider refreshTokenAuthProvider;
  
    @PostMapping("/register")
    public ResponseEntity register(@RequestBody SignUp signupDTO) {
        User user = new User(signupDTO.getUserName(), signupDTO.getPassword());
        userDetailsManager.createUser(user);
  
        Authentication authentication = UsernamePasswordAuthenticationToken.authenticated(user, signupDTO.getPassword(), Collections.EMPTY_LIST);
  
        return ResponseEntity.ok(tokenGenerator.createToken(authentication));
    }
  
  
  
    @PostMapping("/login")
    public ResponseEntity login(@RequestBody Login loginDTO) {
        Authentication authentication = daoAuthenticationProvider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(loginDTO.getUserName(), loginDTO.getPassword()));
  
        return ResponseEntity.ok(tokenGenerator.createToken(authentication));
    }
  
    @PostMapping("/token")
    public ResponseEntity token(@RequestBody Token tokenDTO) {
        Authentication authentication = refreshTokenAuthProvider.authenticate(new BearerTokenAuthenticationToken(tokenDTO.getRefreshToken()));
        Jwt jwt = (Jwt) authentication.getCredentials();
        // check if present in db and not revoked, etc
  
        return ResponseEntity.ok(tokenGenerator.createToken(authentication));
    }
}


Step 15: Create class UserController for user validation through the web JWT Token authorization.

Go to the src > main > java > Controller> and create a UserController class and put the below code.

Java




package com.example.oAuth.controller;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.ResponseEntity;
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.security.core.annotation.AuthenticationPrincipal;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
  
import com.example.oAuth.repository.UserRepository;
import com.example.oAuth.userDocument.User;
import com.example.oAuth.userModel.UserDTO;
  
    @RestController
    @RequestMapping("/api/users")
    public class UserController {
        @Autowired
        UserRepository userRepository;
  
        @GetMapping("/{id}")
        @PreAuthorize("#user.id == #id")
        public ResponseEntity user(@AuthenticationPrincipal User user, @PathVariable String id) {
            return ResponseEntity.ok(UserDTO.from(userRepository.findById(id).orElseThrow()));
        }
    }


Step 16: Create the main class.

Go to the src > main > java > oAuth> and create a class OAuthwithJwtProjectApplication and put the below code.

Java




package com.example.oAuth;
  
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
  
@SpringBootApplication
public class OAuthwithJwtDemoProjectApplication {
  
    public static void main(String[] args) {
        SpringApplication.run(OAuthwithJwtDemoProjectApplication.class, args);
    }
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
  
}


Before running the project, below is the complete pom.xml file. Please cross-verify if you have missed some dependencies.

XML




<?xml version="1.0" encoding="UTF-8"?>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>OAuthwithJWTDemoProject</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>OAuthwithJWTDemoProject</name>
    <description>Demo Project to implememt the OAuth with JWT</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>
        <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.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version><!--$NO-MVN-MAN-VER$-->
        <scope>provided</scope>
    </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
  
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  
</project>


Step 17: Run the application

Application Started

Once run the application you will find the started the OAuthwithJwtDemoProject and its tomcat started on port 8080 once the application run successful then rest the API’s using Postman tool.

Step 18: Run Your AuthController API endpoint.

Register API

Test Your AuthController Endpoint in Postman.

Now open Postman and hit the following URL:

POST : http://localhost:8080/api/auth/register


Refer the output image for the better understanding:

Below is the URL for register API.

POST: http://localhost:8080/api/auth/login


Go to the authorization option and click on the bearer token and give the access token. Go to the body and click on the json format and give the username and password. If the username, password, and token match, give the ok response, and if the below response generates a not found error.

Add the token authorization section then select the bearer token after that add the accesses token to login and accesses the user data.

Token added int Authorization

Then add the user login details in the body section select as raw and choose the JSON format add the user detail then send the request user gain the access when the user login detail additionally JWT details are true otherwise unauthorized the request.

Refer the below image for better understanding:

Login details are added in the body in json format

After the complication project, you can test the project through the Postman tool to see whether the API can generate the token or not, and it can be used to login to the database. Here are the testing images of the project for running on your local machine.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads