Daniel Donev
Daniel Donev

Reputation: 284

Spring secure endpoint with only client credentials (Basic)

I have oauth2 authorization server with one custom endpoint (log out specific user manually as admin) I want this endpoint to be secured with rest client credentials (client id and secret as Basic encoded header value), similar to /oauth/check_token.

This endpoint can be called only from my resource server with specific scope.

  1. I need to check if the client is authenticated.
  2. I would like to be able to add @PreAuthorize("#oauth2.hasScope('TEST_SCOPE')")on the controller`s method.

I could not find any docs or way to use the Spring`s mechanism for client authentication check.

EDIT 1

I use java config not an xml one

Upvotes: 6

Views: 1650

Answers (2)

Daniel Donev
Daniel Donev

Reputation: 284

So I ended up with the following solution

Authentication Manager

public class ClientAuthenticationManager implements AuthenticationManager {

private ClientDetailsService clientDetailsService;
private PasswordEncoder passwordEncoder;

public HGClientAuthenticationManager(ClientDetailsService clientDetailsService, PasswordEncoder passwordEncoder) {
    Assert.notNull(clientDetailsService, "Given clientDetailsService must not be null!");
    Assert.notNull(passwordEncoder, "Given passwordEncoder must not be null!");
    this.clientDetailsService = clientDetailsService;
    this.passwordEncoder = passwordEncoder;
}

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    ClientDetails clientDetails = null;
    try {
        clientDetails = this.clientDetailsService.loadClientByClientId(authentication.getPrincipal().toString());
    } catch (ClientRegistrationException e) {
        throw new BadCredentialsException("Invalid client id or password");
    }
    if (!passwordEncoder.matches(authentication.getCredentials().toString(), clientDetails.getClientSecret())) {
        throw new BadCredentialsException("Invalid client id or password");
    }
    return new OAuth2Authentication(
            new OAuth2Request(null, clientDetails.getClientId(), clientDetails.getAuthorities(), true,
                    clientDetails.getScope(), clientDetails.getResourceIds(), null, null, null),
            null);
}
}

Filter declaration

    private BasicAuthenticationFilter basicAuthenticationFilter() {
    ClientDetailsUserDetailsService clientDetailsUserDetailsService = new ClientDetailsUserDetailsService(
            this.clientDetailsService);
    clientDetailsUserDetailsService.setPasswordEncoder(this.passwordEncoder);
    return new BasicAuthenticationFilter(
            new ClientAuthenticationManager(this.clientDetailsService, this.passwordEncoder));
}

Filter registration

httpSecurity.addFilterBefore(this.basicAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)

WARNING!!! This will prevent any other types of authentication (oauth2, etc.). ONLY Basic authentication is accepted and ONLY for registered clients.

Upvotes: 1

JohanB
JohanB

Reputation: 2148

@PreAuthorize("#oauth2.hasScope('TEST_SCOPE')") On the controller method should be sufficiënt. If the client is not authenticated, no scope is available and the scope check will fail.

If you want, you can use the Spring Security expression @PreAuthorize("isAuthenticated()") to check if a client is authenticated: https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/#el-common-built-in

You could also configure the HttpSecurity instead of working with @PreAuthorize

Upvotes: 0

Related Questions