How to get claims from jwt token in spring webflux

2.6k views Asked by At

I am having a spring webflux based GraphQL application. The application is configured as resourceServer and I am able to access the /graphql endpoint by passing JWT token.

Within the JWT token there are claims that I want to access. I am trying to use the ReactiveSecurityContextHolder.getContext() but getting null every time.

ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.map(Authentication::getPrincipal)
.cast(Jwt.class)

The above code is in the GraphqlQueryResolver class. For graphql I are using the below dependency.

<dependency>
    <groupId>com.graphql-java-kickstart</groupId>
    <artifactId>graphql-spring-boot-starter</artifactId>
    <version>12.0.0</version>
</dependency>

Below is the security configuration.

@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {

        return http
                .csrf(spec -> spec.disable())
                .authorizeExchange().pathMatchers("/api/**").permitAll()
                .anyExchange().authenticated().and()
                .oauth2Login()
                .and()
                .oauth2ResourceServer()
                .jwt().and().and().build();

    }
}

Now I am getting a UUID as part of the access token claim which I want to access on my graphql resolver class. But every time I try to get the JWT using the above code I am getting null.

If I try to get the same in @RestController with @AutheticationPrincipal it works perfectly. But I want to access the same using the SecurityContext in graphql resolver class.

Here is a simple resolver

public class CustomerResolver implements GraphQLQueryResolver {

    public Mono<String> getCustomerById (){

        return ReactiveSecurityContextHolder.getContext()
                .map(ctx -> ctx.getAuthentication())
                .map(authentication -> {
                    if(authentication instanceof OAuth2AuthenticationToken){
                        return ((OAuth2AuthenticationToken) authentication).getPrincipal().getAttributes();
                    } else if(authentication instanceof JwtAuthenticationToken){
                        return ((JwtAuthenticationToken) authentication).getTokenAttributes();
                    }
                    return null;
                }).map(jwt -> String.valueOf(jwt));
    }
}

And the corresponding schema file for graphql

type Query {
    getCustomerById: String
}
1

There are 1 answers

2
Alex On

Try to create minimal example to isolate the issue. The following worked for me with your security configuration but without Okta.configureResourceServer401ResponseBody(http);

@Configuration
@RestController
public class RouteConfig {

    @GetMapping(value = "/api/test")
    public Mono<String> test() {
        return ReactiveSecurityContextHolder.getContext()
                .map(ctx -> ctx.getAuthentication().getPrincipal())
                .cast(Jwt.class)
                .map(jwt -> jwt.getClaimAsString("tenant"));
    }
}

Here is a test using org.springframework.security:spring-security-test

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
class RouteConfigTest {
    @Autowired
    private WebTestClient client;

    @Test
    void test() {
        this.client.mutateWith(jwt())
                .get()
                .uri("/api/test")
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                .expectStatus().isOk()
                .expectBody(String.class).isEqualTo("company");
    }

    private static SecurityMockServerConfigurers.JwtMutator jwt() {
        return mockJwt()
                .jwt(jwt -> jwt.claim("tenant", "company"));
    }
}