title: Spring Security Oauth2 validate client without access token
body: <p>I'm trying to &quot;secure&quot; my oauth2 authorization server. By default anyone can call these methods, then get an access token, which then can be used to call our resource servers.</p>
  <p>What I want to achieve is to secure the authorization server via basic authentication too (so client ID + client secret). This is so only our authorized clients can call these methods.</p>
  <p>The reason we need this is because the standard /oauth/token method is secured so you can only call these via client ID + client secret, but due to backward compatibility we had to add custom login methods (REST, Facebook, Apple etc) and we want to protect these with the same client ID + client secret authentication.</p>
  <p>But I'm not sure how.</p>
  <p>I managed to validate the client ID via a custom AccessDecisionVoter with a custom AccessDecisionManager inside my WebSecurityConfigurerAdapter implementation.</p>
  <p>But I'm sure there is an easier way.</p>
  <p>my current WebSecurityConfigurerAdapter (some parts ommited):</p>
  <pre><code>private final MyAuthenticationEntryPoint customAuthenticationEntryPoint;
  
  private final ClientDetailsService clientDetailsService; //Ouath2 client details service
  
  private static final String[] permitAlls = new String[]{
          &quot;/actuator/**&quot;,
          // -- Swagger UI v2
          &quot;/v2/api-docs&quot;,
          &quot;/swagger-resources&quot;,
          &quot;/swagger-resources/**&quot;,
          &quot;/configuration/ui&quot;,
          &quot;/configuration/security&quot;,
          &quot;/swagger-ui.html&quot;,
          &quot;/webjars/**&quot;,
          // -- Swagger UI v3 (OpenAPI)
          &quot;/v3/api-docs/**&quot;,
          &quot;/swagger-ui/**&quot;
  };
  
  @Bean
  public PasswordEncoder passwordEncoder() {
      return new BCryptPasswordEncoder();
  }
  
  public AccessDecisionManager accessDecisionManager() {
      List&lt;AccessDecisionVoter&lt;?&gt;&gt; voters = new ArrayList&lt;&gt;();
      voters.add(new WebExpressionVoter());
      voters.add(new CustomVoter(clientDetailsService, passwordEncoder()));
      return new UnanimousBased(voters);
  }
  
  @Override
  protected void configure(HttpSecurity http) throws Exception {
      http.csrf().disable()
          .sessionManagement()
          .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
          .and()
          .authorizeRequests()
          .accessDecisionManager(accessDecisionManager())
          .antMatchers(permitAlls).permitAll()
          .antMatchers(HttpMethod.OPTIONS,&quot;/**/*&quot;).permitAll()
          .anyRequest().authenticated()
          .and().exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint).accessDeniedHandler(new CustomAccessDeniedHandler());
  }
  </code></pre>
  <p>Also we have an Oauth2 configuration extending AuthorizationServerConfigurerAdapter:</p>
  <pre><code>@Configuration
  @EnableAuthorizationServer
  public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {
      private final AuthenticationManager authenticationManager;
  
  private final PasswordEncoder passwordEncoder;
  
  private final UserDetailsService userService;
  
  private final DataSource dataSource;
  
  private final ClientDetailsService clientDetailsService;
  
  @Value(&quot;${jwt.signing-key}&quot;)
  private String jwtSigningKey;
  
  public OAuth2Configuration(AuthenticationManager authenticationManager, PasswordEncoder passwordEncoder,
                             UserDetailsService userService, DataSource dataSource, ClientDetailsService clientDetailsService) {
      this.authenticationManager = authenticationManager;
      this.passwordEncoder = passwordEncoder;
      this.userService = userService;
      this.dataSource = dataSource;
      this.clientDetailsService = clientDetailsService;
  }
  
  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
      clients.jdbc(dataSource).passwordEncoder(passwordEncoder).clients(clientDetailsService);
  }
  
  @Override
  public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
      endpoints
          .tokenStore(tokenStore())
          .accessTokenConverter(accessTokenConverter())
          .userDetailsService(userService)
          .authenticationManager(authenticationManager);
  }
  
  @Bean
  public TokenStore tokenStore() {
      return new JdbcTokenStore(dataSource);
  }
  
  @Bean
  public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore, ClientDetailsService clientDetailsService) {
      TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
      handler.setTokenStore(tokenStore);
      handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
      handler.setClientDetailsService(clientDetailsService);
      return handler;
  }
  
  @Bean
  public ApprovalStore approvalStore(TokenStore tokenStore) {
      TokenApprovalStore store = new TokenApprovalStore();
      store.setTokenStore(tokenStore);
      return store;
  }
  
  @Bean
  public JwtAccessTokenConverter accessTokenConverter() {
      JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
      converter.setSigningKey(jwtSigningKey);
      converter.setVerifierKey(jwtSigningKey);
      DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
      tokenConverter.setUserTokenConverter(new MyUserAuthenticationConverter());
      converter.setAccessTokenConverter(tokenConverter);
      return converter;
  }
  
  
  }
  </code></pre>
  <p>There might be some other classes relevant to this, but there might be way via these classes to achieve what I want?</p>
  <p>Thank you</p>