Skip to content

Webflux ServerAuthenticationConverter getting called for permitted paths too #9706

@ilyasdotdev

Description

@ilyasdotdev

I am trying to implement jwt authentication with spring webflux and i am adding filter on SecurityWebFiltersOrder.AUTHENTICATION but the problem is its getting called on permitted urls too.

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(
            ServerHttpSecurity http,
            ReactiveAuthenticationManager jwtAuthenticationManager,
            ServerAuthenticationConverter jwtAuthenticationConverter
    ) {

        AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(jwtAuthenticationManager);
        authenticationWebFilter.setServerAuthenticationConverter(jwtAuthenticationConverter);

        return http
                .addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .httpBasic()
                .disable()
                .csrf()
                .disable()
                .formLogin()
                .disable()
                .logout()
                .disable()
                .exceptionHandling()
                .authenticationEntryPoint((exchange, e) -> {
                    if (e.getCause() instanceof AccessDeniedException) {
                        throw new ForbiddenException(e);
                    }
                    throw new UnauthorizedException(e);
                })
                .and()
                .authorizeExchange()
                .pathMatchers(
                        "/api/auth/login"
                ).permitAll()
                .pathMatchers("/api/user/**").hasRole(RolesList.SUPER_ADMIN.name())
                .pathMatchers("/api/auth/password").hasAnyRole(RolesList.SUPER_ADMIN.name(), RolesList.ADMIN.name())
                .pathMatchers("/api/**").authenticated() //entire server side app will be kept here
                .anyExchange().permitAll() // every else path should be permitted
                .and()
                .build();
    }
}
@Component
public class JwtServerAuthenticationConverter implements ServerAuthenticationConverter {

    private static final String AUTH_HEADER_VALUE_PREFIX = "Bearer ";

    @Override
    public Mono<Authentication> convert(ServerWebExchange exchange) {
        System.out.println("convert called");
        return Mono.justOrEmpty(exchange)
                .flatMap(serverWebExchange -> Mono.justOrEmpty(
                        serverWebExchange
                                .getRequest()
                                .getHeaders()
                                .getFirst(HttpHeaders.AUTHORIZATION)
                        )
                )
                .filter(header -> !header.trim().isEmpty() && header.trim().startsWith(AUTH_HEADER_VALUE_PREFIX))
                .map(header -> header.substring(AUTH_HEADER_VALUE_PREFIX.length()))
                .map(token -> new UsernamePasswordAuthenticationToken(token, token))
                ;
    }
}
@Component
public class JwtAuthenticationManager implements ReactiveAuthenticationManager {

    private final JWTProperties jwtProperties;
    private final ObjectMapper objectMapper;

    public JwtAuthenticationManager(JWTProperties jwtProperties, ObjectMapper objectMapper) {
        this.jwtProperties = jwtProperties;
        this.objectMapper = objectMapper;
    }

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        return Mono.just(authentication)
                .map(auth -> JWTHelper.loadAllClaimsFromToken(auth.getCredentials().toString(), jwtProperties.getSecret()))
                .onErrorResume(throwable -> Mono.error(new UnauthorizedException()))
                .map(claims -> objectMapper.convertValue(claims, JWTUserDetails.class))
                .map(jwtUserDetails ->
                        new UsernamePasswordAuthenticationToken(
                                jwtUserDetails,
                                authentication.getCredentials(),
                                jwtUserDetails.getGrantedAuthorities()
                        )
                )
                ;
    }
}

As you can see /api/auth/login path should not check the jwt token but when i hit it i can see convert called on the console

while i am assuming that is should only get called for authenticated urls.

Metadata

Metadata

Assignees

Labels

for: stackoverflowA question that's better suited to stackoverflow.com

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions