breakline
breakline

Reputation: 6073

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?

Upvotes: 11

Views: 26177

Answers (4)

Hasson
Hasson

Reputation: 1914

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.

Upvotes: 3

rohit prakash
rohit prakash

Reputation: 575

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

}

Upvotes: 3

Luke Cheung
Luke Cheung

Reputation: 497

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

Spring Security Oauth - Custom format for OAuth2Exceptions

Upvotes: 1

Karthik
Karthik

Reputation: 5040

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.

Upvotes: 8

Related Questions