Results 1 to 8 of 8

Thread: how can i add custom error response to auth server?

  1. #1
    Join Date
    May 2012
    Posts
    15

    Default how can i add custom error response to auth server?

    current oauth2.0 server's error response body format is:
    - error (invalid_client, unauthorized_client, etc)
    - error_description (..)

    i need to customize this error response such as facebook for body:
    - {error:{code:'', message:'', type:''}}

    so it'will be consistent with our api server error response.

    do i need custom
    - OAuth2ExceptionSerializer
    - OAuth2ExceptionDeserializer
    - DefaultOAuth2ExceptionRenderer
    - or convertor?

    any help will be appriciated.

  2. #2
    Join Date
    Jun 2005
    Posts
    4,232

    Default

    If I were you I would try and use an HttpMessageConverter. There are a few places where exceptions might be rendered (including and probably for your purposes most importantly DefaultOAuth2ExceptionRenderer) and having one converter implementation should cover it all.

  3. #3
    Join Date
    May 2012
    Posts
    15

    Default

    Quote Originally Posted by Dave Syer View Post
    If I were you I would try and use an HttpMessageConverter. There are a few places where exceptions might be rendered (including and probably for your purposes most importantly DefaultOAuth2ExceptionRenderer) and having one converter implementation should cover it all.
    I've done with auth/resource server side messege converting.

    my config is like:

    Custom convertor for exception renderer:
    <bean id="gdpExceptionRenderer" class="org.springframework.security.oauth2.provide r.error.DefaultOAuth2ExceptionRenderer" >
    <property name="messageConverters">
    <list>
    <bean class="com.nhncorp.playnet.gdp.oauth.convertor.Gdp AuthExceptionMessageConvertor">
    <property name="supportedMediaTypes">
    <list>
    <bean class="org.springframework.http.MediaType">
    <constructor-arg value="application" />
    <constructor-arg value="json" />
    </bean>
    <bean class="org.springframework.http.MediaType">
    <constructor-arg value="text" />
    <constructor-arg value="html" />
    </bean>
    </list>
    </property>
    </bean>
    </list>
    </property>
    </bean>

    Inject above renderer to entry point(401) and error handler(403):
    <bean id="oauthAuthenticationEntryPoint" class="com.nhncorp.playnet.gdp.oauth.security.GdpA uthenticationEntryPoint">
    <property name="realmName" value="GDP Platform" />
    <property name="exceptionRenderer" ref="gdpExceptionRenderer" />
    </bean>

    <bean id="oauthAccessDeniedHandler" class="com.nhncorp.playnet.gdp.oauth.handler.GdpAc cessDeniedHandler" >
    <property name="exceptionRenderer" ref="gdpExceptionRenderer" />
    </bean>

    so message original error message is converted to our custom format:
    {"error":"invalid_client", message:"..."} ---> {"error":{"message":"Error validating client secret.","type":"OAuthException","code":1001}}

    so now it works.

    but in client side is there a new problem.

    The problem is that when token end point returns 401 for a new token request (invalid_error, actually when client secret is wrong), and custom error message in response body with json format, i can't read response body in custom HttpMessageConvertor, because OAuth2AccessTokenSupport's restTemplate send the token request in POST and steamming mode, that causes java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode.


    convertor code is like:

    @Override
    public OAuth2Exception read(Class<? extends OAuth2Exception> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
    log.debug("read start");
    TypeReference<HashMap<String,MockGdpException>> typeRef = new TypeReference<HashMap<String, MockGdpException>>(){};

    ClientHttpResponse response = (ClientHttpResponse) inputMessage;

    InputStream in = response.getBody();

    HashMap<String,MockGdpException> messageWrapper = objectMapper.readValue(in, typeRef);

    MockGdpException mockGdpException = messageWrapper.get("error");

    OAuth2Exception oe = MockAuthExceptionConvertor.convertToOAuth2Exceptio n(mockGdpException);
    return oe;
    }

    the exception is thrown at response.getBody().


    how can i handle this type of error?

  4. #4
    Join Date
    Jun 2005
    Posts
    4,232

    Default

    The default HttpRequestFactory is broken in respect of handling redirects and failures. You can probably make it work just by using the HttpComponentsClientHttpRequestFactory in your RestTemplate.

  5. #5
    Join Date
    May 2012
    Posts
    15

    Default

    Thanks Dave~
    HttpComponentsClientHttpRequestFactory works great with basic authentication.

    I've overrrided AuthorizationCodeAccessTokenProvider and setted my restTemplate with HttpComponentsClientHttpRequestFactoryBasicAuth which supports auth caching for preemtive authentication.

    sample code here:

    Code:
    public class MockAuthorizationCodeAccessTokenProvider extends AuthorizationCodeAccessTokenProvider {
        private RestTemplate restTemplate;
        private HttpComponentsClientHttpRequestFactoryBasicAuth requestFactory;
        
        public MockAuthorizationCodeAccessTokenProvider() {
            this.restTemplate = new RestTemplate();
            
            HttpHost targetHost = new HttpHost("local-gdp.onlinegame.com", 80, "http");
            DefaultHttpClient httpClient = new DefaultHttpClient();
            
            BasicCredentialsProvider provider = new BasicCredentialsProvider();
            provider.setCredentials(new AuthScope(targetHost, AuthScope.ANY_REALM, "basic"), new UsernamePasswordCredentials("mocksite", "secret"));
            httpClient.setCredentialsProvider(provider);
            
            AuthCache authCache = new BasicAuthCache();
            BasicScheme basicAuth = new BasicScheme();
            
            authCache.put(targetHost, basicAuth);
            BasicHttpContext localContext = new BasicHttpContext();
            localContext.setAttribute(ClientContext.AUTH_CACHE, authCache);    
            
            this.requestFactory = new HttpComponentsClientHttpRequestFactoryBasicAuth(httpClient);
            
            this.requestFactory.setHttpContext(localContext);
            this.restTemplate.setRequestFactory(requestFactory);
            
            this.restTemplate.setErrorHandler(getResponseErrorHandler());
        }
        
        @Override
        protected OAuth2AccessToken retrieveToken(MultiValueMap<String, String> form, HttpHeaders headers,
                OAuth2ProtectedResourceDetails resource) throws OAuth2AccessDeniedException {
    
    
            try {
                // Prepare headers and form before going into rest template call in case the URI is affected by the result
                //authenticationHandler.authenticateTokenRequest(resource, form, headers);
    
    
                return getRestTemplate().execute(getAccessTokenUri(resource, form), getHttpMethod(),
                        getRequestCallback(resource, form, headers), getResponseExtractor(), form.toSingleValueMap());
    
    
            }
            catch (OAuth2Exception oe) {
                throw new OAuth2AccessDeniedException("Access token denied.", resource, oe);
            }
            catch (RestClientException rce) {
                throw new OAuth2AccessDeniedException("Error requesting access token.", resource, rce);
            }
    
    
        }
        
        @Override
        protected RestTemplate getRestTemplate() {
            return restTemplate;
        }
    
    
    }

    you can notice that authenticationHandler.authenticateTokenRequest(res ource, form, headers); is commented, because it overwrites authentication header. but that'd be no problem, the credentials are same anyway.
    Last edited by enkhchuluun; Jul 21st, 2012 at 11:51 AM.

  6. #6
    Join Date
    Jun 2005
    Posts
    4,232

    Default

    That's nice. I'm surprised you had to extend a framework class though. Couldn't you just inject a customized RestTemplate into an existing AccessTokenProvider?

  7. #7
    Join Date
    May 2012
    Posts
    15

    Default

    Quote Originally Posted by Dave Syer View Post
    That's nice. I'm surprised you had to extend a framework class though. Couldn't you just inject a customized RestTemplate into an existing AccessTokenProvider?
    Yep, there was no way to inject custome RestTemplate, because there' no public setter method or constructor on both
    AccessTokenProvider interface and OAuth2AccessTokenSupport abstract class.

    i guess you can consider to add.

    Thanks~

  8. #8
    Join Date
    Jun 2005
    Posts
    4,232

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •