https://stackoverflow.com/questions/76360972/spring-security-when-i-try-to-get-jwt-token-second-time-it-returns-forbidden

I enabled spring security. I have 2 users in my userDetails service. I have an authentication endpoint /api/v1/auth/authenticate. This endpoint is not secured.

I start the application and hit send button in postman. It returns the JWT token. When I click send right after, it returns 403.

After these steps, I entered the credentials of the second user in postman request. I hit the send request button. JWT token returned normally. When I clicked send button the second time, it returns 403.

I am able to access secured endpoints normally using the JWT.

I restarted the server, Again I am able to call and get JWT token but I can get it only once, the other attempts to generate JWT token results in a 403 response.

When I debugged the application, it gets bad credentials exception in this line

authenticationManager.authenticate(
   new UsernamePasswordAuthenticationToken(
        request.getEmail(), request.getPassword()
    )
);

it doesn't print the exception to the console. Even if the credentials are correct (I don't change request body between the first and second attempt to get JWT.) the response is 403 in the second attempt.

Why is that?

I use spring boot 3.1.0

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.1.0</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

my userdetails service is:

@Component
public class MyUserDetailsService implements UserDetailsService {

    private final static List<UserDetails> APPLICATION_USERS = Arrays.asList(
            new User(
                "ylozsoy@gmail.com",
                "password",
                Collections.singleton(new SimpleGrantedAuthority("ROLE_ADMIN"))
            ),
            new User(
                    "yilmazozsoy@gmail.com",
                    "password2",
                    Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))
            )
    );

    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        return APPLICATION_USERS
                .stream()
                .filter(u -> u.getUsername().equals(email))
                .findFirst()
                .orElseThrow(() -> new UsernameNotFoundException("No user was found for email: " + email));
    }
}
@Override
protected void doFilterInternal(
        HttpServletRequest request,
        HttpServletResponse response,
        FilterChain filterChain) throws ServletException, IOException {
    String authHeader = request.getHeader(AUTHORIZATION);
    String userEmail;
    String jwtToken;
    if (authHeader == null || !authHeader.startsWith("Bearer")) {
        filterChain.doFilter(request, response);
        return;
    }
    jwtToken = authHeader.substring(7);
    userEmail = jwtService.extractUsername(jwtToken);
    if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = userDetailsService.loadUserByUsername(userEmail);
        if(jwtService.isTokenValid(jwtToken, userDetails)) {
            UsernamePasswordAuthenticationToken authToken =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
        }
    }
    filterChain.doFilter(request, response);
}
@PostMapping("/authenticate")
public ResponseEntity<String> authenticate(@RequestBody AuthenticationRequest request){
    authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword())
    );
    final UserDetails user = userDetailsService.loadUserByUsername(request.getEmail());
    if (user != null) {
        return ResponseEntity.ok(jwtService.generateToken(user));
    }
    return ResponseEntity.status(400).body("Some error has occured");
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.csrf((csrf) -> csrf.disable());
    http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
    http.authorizeHttpRequests((requests) -> {
        requests.requestMatchers(antMatcher("/api/v1/auth/**"))
                .permitAll()
                .anyRequest()
                .authenticated();
    });
    http.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
    http.authenticationProvider(authenticationProvider());
    http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
    return http.build();
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.springbootdemoweb</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>


        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.2.7</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.5-b10</version>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Comment From: wilkinsona

Please don't cross-post a question here and on Stack Overflow. It risks people duplicating effort as someone may try to help you in one place when you've already been helped in another. Stack Overflow is also a better place to ask questions. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.

Comment From: yozsoy

This kind of a behaviour looked like a bug to me that is why i posted it here. Stackoverflow won't help about bugs.

Comment From: yozsoy

It is breaking here SpringBoot When I try to get JWT token second time, it returns forbidden

at the firs call this looks like : SpringBoot When I try to get JWT token second time, it returns forbidden

At the second call, password is gone SpringBoot When I try to get JWT token second time, it returns forbidden

Comment From: yozsoy

It looks like that spring boot tries to protect the password but completely deletes it

Comment From: wilkinsona

It's not clear to me that it's a bug as neither this issue nor the question on Stack Overflow have provided enough information for me to understand what you're trying to do and how the failure is occurring. If you're certain that it's a bug and that Stack Overflow won't help I think you should delete your question to avoid wasting people's time.

DaoAuthenticationProvider is part of Spring Security which is managed as a separate project. If you are certain that it contains a bug, please open an issue there. When doing so, you should ideally provide a complete yet minimal example that reproduces the problem. Rather than snippets of code in issue comments, that should be something that can be easily run to reproduce the problem. A project generated on https://start.spring.io is a good place to start.

Comment From: yozsoy

This is the complete project:

Archive.zip

There is no way to tell if it is a bug or something else for sure.