Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Oauth2 - custom exception handler

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?

like image 889
breakline Avatar asked Jul 27 '15 18:07

breakline


3 Answers

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.

like image 171
Karthik Avatar answered Nov 08 '22 22:11

Karthik


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);
    }

}
like image 3
rohit prakash Avatar answered Nov 08 '22 21:11

rohit prakash


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.
like image 3
Hasson Avatar answered Nov 08 '22 21:11

Hasson