16

We have an application which is using spring-security-oauth2:1.0. I was trying to change it to a newer version, spring-security-oauth2:2.0.7.RELEASE. Some classes were removed, some package structure is changed, I managed to sort out all those things and I was able to start the server without any issue. But I am facing a strange issue here.

With OAuth2 - 1.0 version, when the user logs in we used to do a GET request on /oauth/token, For example :

http://localhost:8080/echo/oauth/token?grant_type=password&client_id=ws&client_secret=secret&scope=read,write&username=john@abc.com&password=password123

and It used to work just fine.

When I try the same thing, First of all I am not able to make a GET request because of the logic in TokenEndPoint.java

private Set<HttpMethod> allowedRequestMethods = new HashSet<HttpMethod>(Arrays.asList(HttpMethod.POST));

@RequestMapping(value = "/oauth/token", method=RequestMethod.GET)
public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, @RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
    if (!allowedRequestMethods.contains(HttpMethod.GET)) {
        throw new HttpRequestMethodNotSupportedException("GET");
    }
    return postAccessToken(principal, parameters);
}

I have tried to make a POST request same as above URL, but I get InsufficientAuthenticationException with the error message

There is no client authentication. Try adding an appropriate authentication filter

This is because of the following POST request controller in TokenEndpoint.java. When I debug, I see that principal is null.

@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
    public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
    Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        //principal is null here
        if (!(principal instanceof Authentication)) {
            throw new InsufficientAuthenticationException(
                    "There is no client authentication. Try adding an appropriate authentication filter.");
        }
     .............
 }

I have an authentication filter and it worked well when I used version 1.0. This is the relevant prats of my config:

    <authentication-manager xmlns="http://www.springframework.org/schema/security">
        <authentication-provider user-service-ref="userDetailsService"/>
    </authentication-manager>

   <bean id="userDetailsService" class="com.hcl.nc.service.UserDetailsService">
        <constructor-arg><ref bean="sessionFactory" /></constructor-arg>
    </bean>

I always thought that the request will be authenticated by authentication-provider and goes to token-endpoint but that does not seem to be the correct flow. After debugging the application with version 2.0.7, now I really doubt my understanding about the flow.

Could somebody please explain why it worked in previous version and why it's not working now?

Do I have do to something different to get a OAuth token??

NOTE: I have already checked these questions : here, here, here. But I was not able to find the correct solution.

Community
  • 1
  • 1
Karthik
  • 4,950
  • 6
  • 35
  • 65

6 Answers6

7

I don't know the previous version, but I know a bit about 2.0.7.

I suspect your problem is that your TokenEndpoint security tries to authenticate your clients against your user service.

The TokenEndpoint is protected by a BasicAuthenticationFilter. By default this filter would use an AuthenticationManager instance, which itself holds an AuthenticationProvider, which itself depends on an instance of UserDetailsService. The trick is that this particular instance of UserDetailsService must be client based, not user based : that's why there is a ClientDetailsUserDetailsService, which adapts ClientDetailsService to UserDetailsService.

Normally all this stuff is already done by default when you use the framework's configuration classes AuthorizationServerConfigurerAdapter, @EnableAuthorizationServer, etc..

Michael Técourt
  • 3,457
  • 1
  • 28
  • 45
1

I had the same problem and my application.yml had this line:

servlet:
    path: /auth

so the token address was: /auth/oauth/token

I remove the path from application.yml so the token path became:

/oauth/token

And everything works fine.

I hope this help

shim
  • 9,289
  • 12
  • 69
  • 108
  • Though this is not a solution I was looking for, but gave me direction and rootcause of the exception. If anyone knows how to work with oauth and servlet context, please let me know. – skvp Nov 19 '18 at 11:33
1

One of the problems of the following error, can be that authentication was not performed. I have encountered this problem with older implementation of Spring.

verify that:

TokenEndpoint -> postAccessToken method. Check if Principal is not null. If it is null it means that Basic Authroziation was not performed.

One of the solution to add filter was to use:

@Configuration
public class FilterChainInitializer extends AbstractSecurityWebApplicationInitializer {

}

More information about AbstractSecurityWebApplicationInitializer can be found in Spring docs

Damiani
  • 259
  • 3
  • 7
1

The problem can be because of opening all requests. You should remove it.

 @Override
public void configure(WebSecurity web) throws Exception {
    web
            .ignoring()
            .antMatchers("/**");
}
Vahan Yeghyan
  • 487
  • 5
  • 7
  • This is a good answer for me. Anyone who has this issue should check the implementation of the WebSecurityConfigurerAdapter first. See if you open the URL like "/oauth/token" or "/oauth/**" or "/**", in the function above. Doing so, makes all the oauth end points expose to outside without security checks, which defeat the purpose of Spring Security Oauth. So, make your URL exclusion explicit and do not expose oauth endpoints should fix this error. – Wenlong Jiang Nov 26 '22 at 03:06
0

in my case, i found this config:

security.allowFormAuthenticationForClients(); // here

then post this http://localhost:8081/sso/oauth/token?client_id=unity-client&client_secret=unity&grant_type=authorization_code&code=Yk4Sum&redirect_uri=http://localhost:8082/sso-demo/passport/login

its works for me, try it

@Configuration
@EnableAuthorizationServer
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {

    private static final Logger log = LoggerFactory.getLogger(Oauth2Config.class);

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients(); // here 
    }

    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception { // @formatter:off
        clients.inMemory()
                .withClient("unity-client")
                .secret("unity")
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                .scopes("foo", "read", "write")
                .accessTokenValiditySeconds(3600) // 1 hour
                .refreshTokenValiditySeconds(2592000) // 30 days
        ;
    } // @formatter:on

    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    }

}
0

I am following this tutorial - Practical Guide to Building an API Back End with Spring Boot'. See https://www.infoq.com/minibooks/spring-boot-building-api-backend , But with the latest SpringBoot Version(2.7)

and I run into this problem:

org.springframework.security.authentication.InsufficientAuthenticationException: There is no client authentication. Try adding an appropriate authentication filter. at org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(TokenEndpoint.java:91) ~[spring-security-oauth2-2.3.5.RELEASE.jar:na]

My solution/fix was to annotate WebSecurityGlobalConfig with @EnableWebSecurity because in the original course this annotation was missing. So adding this annotaiton has fixed the error for me.

razvang
  • 1,055
  • 11
  • 15