10

In Spring Security Oauth2 based authentication when the client sends an access token which needs to be refreshed, the DefaultTokenServices class throws an InvalidTokenException (see at line 235):

https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/token/DefaultTokenServices.java

the output when this happens is something like:

{"error":"invalid_token","error_description":"Invalid access token: a0cb5ab9-7281-46bd-a9a2-796a04a906c9"
}

I'd like to change this output but I got lost. Some other answer suggested setting up a custom exceptionRenderer but this didn't work either, my custom exception renderer never gets called in these cases.

Also there's something called an exception translator but they werent called either in any case.

Part of my spring config:

<bean id="clientAuthenticationEntryPoint"
      class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <property name="typeName" value="Basic"/>
    <property name="exceptionRenderer" ref="myExceptionRenderer" />
</bean>


<bean id="oauthAuthenticationEntryPoint"
      class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
      <property name="exceptionRenderer" ref="myExceptionRenderer" />
      <property name="exceptionTranslator" ref="listyOauthExceptionTranslator" />
</bean>

<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" >
 <property name="exceptionRenderer" ref="myExceptionRenderer" />
 <property name="exceptionTranslator" ref="myExceptionTranslator" />
</bean>

<bean id="myExceptionRenderer" class="com.example.exceptions.MyOauth2ExceptionRenderer" />

<bean id="myExceptionTranslator" class="com.example.exceptions.MyOauth2ExceptionTranslator" />

The exception renderer:

public class MyExceptionRenderer implements OAuth2ExceptionRenderer {

@Override
public void handleHttpEntityResponse(HttpEntity<?> responseEntity, ServletWebRequest webRequest) throws Exception {
    System.out.println("Thrown exception");
}

}

I also added a custom Exception Mapper which should get ALL the exceptions, but since I assume its another servlet this doesnt really work in this case?

@Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {

@Override 
public Response toResponse(Throwable ex) {
    System.out.println("MAPPING EXCEPTION");
    return Response.status(200).entity().build();
}
}

I could catch cases of AuthenticationException, but not any of the InvalidTokenExceptions.

Any help regarding this? Where does Spring actually catch this InvalidTokenException and how can I set it up so I can provide a custom output?

breakline
  • 5,776
  • 8
  • 45
  • 84

4 Answers4

8

InvalidTokenException extends ClientAuthenticationException. So you can create your own exception by extending ClientAuthenticationException and throw this instead of InvalidTokenException

public class CustomException extends ClientAuthenticationException {

    public CustomException(String msg, Throwable t) {
        super(msg, t);
    }

    public CustomException(String msg) {
        super(msg);
    }
    @Override
    public String getOAuth2ErrorCode() {
        return "my_custom_exception";
    }
}

like

throw new CustomException("Invalid access token: " + accessTokenValue);

In the error that is thrown by InvalidTokenException

 {"error":"invalid_token","error_description":"Invalid access token: a0cb5ab9-7281-46bd-a9a2-796a04a906c9"}

invalid_token is returned by getOAuth2ErrorCode() method of InvalidTokenException and Invalid access token: a0cb5ab9-7281-46bd-a9a2-796a04a906c9 is the message that you give when you throw the exception.

If you throw

 throw new CustomException("This is my custom exception");

the error would be shown as

{"error":"my_custom_exception","error_description":"This is my custom exception"}

my_custom_exception is coming from getOAuth2ErrorCode() of CustomException.

Karthik
  • 4,950
  • 6
  • 35
  • 65
  • I can't use a custom exception, since its the Spring Security Servlet and my exception mapper never gets called. I found this also, but I don't know how to configure this via xml? – breakline Jul 27 '15 at 19:04
  • @breakline what do you mean by you can't use a custom exception? Throw this custom exception in your `DefaultTokenServices`. – Karthik Jul 27 '15 at 19:08
  • @breakline the issue you posted talks about changing the `OAuth2EntryPoint`. If you just have to change the exception, you can do it like this. – Karthik Jul 27 '15 at 19:11
  • If you overwrite DefaultTokenServices.loadAuthentication you either need to throw an AuthenticationException or an InvalidTokenException. If you overwrite the first you get an 500 server error I assume which is the default handler in the servlet. If you overwrite the latter nothing happens since its still a RuntimeException and the Spring Security Servlet deals with it – breakline Jul 27 '15 at 19:12
  • that's not necessary, just check this : http://i.stack.imgur.com/uURjt.png I just added this customexception class and it works fine – Karthik Jul 27 '15 at 19:19
  • But did you overwrite everything? Like, implementing a whole custom Token Services class? And what catches the exception then? – breakline Jul 27 '15 at 19:27
  • Why do you want to catch `InvalidTokenException`? It's not supposed to catch. If you want to catch, why are you throwing it here in first place? – Karthik Jul 27 '15 at 19:29
  • @breakline yes, What I have shown you is a cutom token service, but you should be able to do the same thing in default token service also. – Karthik Jul 27 '15 at 19:32
  • @breakline I am glad that it helped :) – Karthik Jul 27 '15 at 19:37
  • @Karthik My probelm is just to to add error_code (401) also along with error and error_description. How would I do that? – manocha_ak Jul 19 '16 at 07:47
  • 1
    @Karthik How can i configure this CustomException class in spring-security xml file??please tell me – KJEjava48 Aug 02 '16 at 06:09
  • I'm able to change the "error" message in the response body (by creating and throwing a custom exception), but is there any way to change the response code that gets returned? It seems to only want to return a 400, but I'd like to to return 429 (too many requests) – user2121620 Oct 03 '18 at 19:22
  • @user2121620 You should be able to do that in a global exception handler – Karthik Nov 06 '18 at 18:48
3

The answer does not really deliver custom implementation, a custom response will be a point in code where I can access the default response and I can send a POJO instead of it, for example if you want to change error_description to error_info or anything else, or you may want to add more variables to the response. The solution does exist, but I think it is painful to implement to say the least, as I copy it from here:

This problem has been solved. Follow the workaround below:

  1. Extend OAuth2Exception to a new class, such as CustomOAuth2Exception. In the custom class, add some specific properties.
  2. custom DefaultWebResponseExceptionTranslator and register the custom translator in AuthorizationServerConfiguration.
  3. custom two jackson serializers annotated in OAuth2Exception and annotated your CustomOAuth2Exception with the two custom serializers.
  4. use ObjectMapper to override initial serializers with custom serializers.
Denis Zavedeev
  • 7,627
  • 4
  • 32
  • 53
Hasson
  • 1,894
  • 1
  • 21
  • 25
3

for overriding

{"error":"invalid_token","error_description":"Invalid access token: a0cb5ab9-7281-46bd-a9a2-796a04a906c9"
}

you need to inherit ResourceServerConfigurerAdapter and override public void configure(final ResourceServerSecurityConfigurer config)

sample code

package com.org.security;

import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
import org.springframework.stereotype.Component;


@Component
public class CustomWebResponseExceptionTranslator extends DefaultWebResponseExceptionTranslator {

    /**
     * Modify OAuth2.0 Error Response
     * @param e
     * @return ResponseEntity<OAuth2Exception>
     * @throws Exception
     */

    @Override
    public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
        ResponseEntity responseEntity = super.translate(e);
        OAuth2Exception auth2Exception = (OAuth2Exception)responseEntity.getBody();
        if (auth2Exception != null) {
            auth2Exception.addAdditionalInformation("data", null);
            auth2Exception.addAdditionalInformation("message", auth2Exception.getMessage());
            auth2Exception.addAdditionalInformation("statusCode", String.valueOf(auth2Exception.getHttpErrorCode()));
        }
        return new ResponseEntity<OAuth2Exception>(auth2Exception, responseEntity.getHeaders(), responseEntity.getStatusCode());
    }
}





package com.org.security;

import com.org.exception.CustomAuthExceptionEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;


@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private ResourceServerTokenServices tokenServices;

    @Autowired
    private WebResponseExceptionTranslator oauth2ResponseExceptionTranslator;

    @Override
    public void configure(final ResourceServerSecurityConfigurer config) {
        OAuth2AccessDeniedHandler auth2AccessDeniedHandler = new OAuth2AccessDeniedHandler();
        auth2AccessDeniedHandler.setExceptionTranslator(oauth2ResponseExceptionTranslator);
        OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
        authenticationEntryPoint.setExceptionTranslator(oauth2ResponseExceptionTranslator);
        config.tokenServices(tokenServices).accessDeniedHandler(auth2AccessDeniedHandler).authenticationEntryPoint(authenticationEntryPoint);
    }

}
rohit prakash
  • 565
  • 6
  • 12
1

This my solution to custom AuthorizationServer and ResourceServer exception. May be it will help.

Spring Security Oauth - Custom format for OAuth2Exceptions

Luke Cheung
  • 479
  • 6
  • 8