Authenticate Keycloak JWT token outside Spring Boot filter chain

336 Views Asked by At

I have a web application (Spring Boot + Angular). The backend authentication is implemented using Keycloak (JWT).

I need to create a GraphQL subscription using Apollo and the subscription should be authenticated/authorized. The subscription is using websocket and regrettably it is impossible to add authorization headers to the initial HTTP request. There are two ways in which the websocket can be authenticated: 1) insert the JWT token into the request URI, and 2) add token to the init message.I would prefer to use the second way as I do not want the token to be stored in any logs.

The request is being created with this code:

const auth = this.injector.get(AuthenticationService);
connections
const wsLink = new GraphQLWsLink(createClient({
  url: 'ws://localhost:9090/web-service/graphql_ws',
  connectionParams: {
      Authorization: 'Bearer ' + auth.getLoginDataFromStorage().access_token
  }
}))


const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: ApolloLink.from([
    middleware,
    errorLink,
    split(
        // split based on operation type
      ({ query }) => {
        const def = getMainDefinition(query)
        return def.kind === 'OperationDefinition' && def.operation === 'subscription'
      },
      wsLink,
      httpLink
    )
  ])
  ,
  defaultOptions: {
    watchQuery: {
      fetchPolicy: 'no-cache'
    },
    query: {
      fetchPolicy: 'no-cache'
    }
  },
});

I have created a WebSocketGraphQlInterceptor in order to retrieve the token during the connection initialisation, however I struggle with authenticating the session using this token.

Interceptor:

@Configuration
public class SubscriptionInterceptor implements WebSocketGraphQlInterceptor
{
    @Override
    public Mono<Object> handleConnectionInitialization(
            WebSocketSessionInfo sessionInfo, Map<String, Object> connectionInitPayload)
    {
        var authToken = connectionInitPayload.get("Authorization").toString();
        return Mono.just(connectionInitPayload);
    }

    @Override
    public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain)
    {
        List<String> token = request.getHeaders().getOrEmpty("Authorization");
        return chain.next(request)
                .contextWrite(context -> context.put("Authorization", token.isEmpty() ? "" : token.get(0)));
    }
}

Security configuration:

@Configuration
@EnableWebSecurity
@ConditionalOnProperty(value = "keycloak.enabled", matchIfMissing = true)
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {



   @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
 
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http
          .csrf().disable()
          .authorizeRequests()
          .antMatchers("/api/**").authenticated()
          .anyRequest().permitAll();
    }
}

How would I be able to authenticate the user using the JWT token in the interceptor?

1

There are 1 best solutions below

0
On

FYI, hope help -

  @Override
    public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
        String token = request.getHeaders().getFirst("client-token");
        String staffName = request.getHeaders().getFirst("staffname");
       
        APPClient client = SecurityHelper.getInstance().validate(token);
        if (Strings.isNullOrEmpty(token) || client == null) {
            GraphQLError error =
                    GraphqlErrorBuilder.newError().message("No privileges").location(SourceLocation.EMPTY).build();
            ExecutionResult executionResult = new ExecutionResultImpl(error);

            ExecutionInput.Builder inputBuilder = new ExecutionInput.Builder();
            inputBuilder.query(request.getDocument());
            inputBuilder.extensions(request.getExtensions());
            inputBuilder.executionId(request.getExecutionId());
            inputBuilder.operationName(request.getOperationName());
            
            ExecutionGraphQlResponse defaultExecutionGraphQlResponse =
                    new DefaultExecutionGraphQlResponse(inputBuilder.build(), executionResult);
            WebGraphQlResponse resp = new WebGraphQlResponse(defaultExecutionGraphQlResponse);
            return Mono.fromSupplier(() -> resp);
        }
return chain
                .next(request) 
                .doOnNext(response -> {
                }); response
}