Search code examples
spring-securityoauth-2.0jersey-2.0spring-security-oauth2

Customise oath2 token request to accept extra data


I am using jersey and spring-oauth2 with spring security. My app is working fine with end points "/oauth/token".

I want to change the endpoints to accept more data. The requirement is, I want to send more details to the token API (i.e. the device details OS, phone/tablet/web etc.). So, I want to override the endpoint and if authentication is successful, I want to store that extra information in database.

I could not find anything related to changing the API in such a way. Can someone help?


Solution

  • I have found a solution by writing a wrapper controller and assigning default tokenEndpoint bean

    @FrameworkEndpoint
    public class LoginContrller{
    
    private static Logger logger = org.slf4j.LoggerFactory.getLogger(LoginContrller.class);
    private WebResponseExceptionTranslator providerExceptionHandler = new DefaultWebResponseExceptionTranslator();
    
    @Autowired
    private UserManager userManager;
    
    @Autowired
    TokenEndpoint tokenEndPoint;
    
    @RequestMapping(value = "/user/login", method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON)
    public ResponseEntity<OAuth2AccessToken>  postAccessToken(Principal principal, @RequestParam
    Map<String, String> parameters,@RequestBody(required=false) LoginModel loginModel) throws HttpRequestMethodNotSupportedException {
        ResponseEntity<OAuth2AccessToken> response = tokenEndPoint.postAccessToken(principal, parameters);
        if(!isRefreshTokenRequest(parameters)){
            if(loginModel!=null){
                loginModel.setUsername(parameters.get("username"));
                try {
                    userManager.loginUser(loginModel);
                } catch (UserNotFoundException e) {
                    logger.warn("Exception in custom login {} ",e);
                }
            }
        }
        return response;
    }
    
    private boolean isRefreshTokenRequest(Map<String, String> parameters) {
        return "refresh_token".equals(parameters.get("grant_type")) && parameters.get("refresh_token") != null;
    }
    
    private boolean isAuthCodeRequest(Map<String, String> parameters) {
        return "authorization_code".equals(parameters.get("grant_type")) && parameters.get("code") != null;
    }
    
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public void handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) throws Exception {
        logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage());
        throw e;
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<OAuth2Exception> handleException(Exception e) throws Exception {
        logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage());
        return getExceptionTranslator().translate(e);
    }
    
    @ExceptionHandler(ClientRegistrationException.class)
    public ResponseEntity<OAuth2Exception> handleClientRegistrationException(Exception e) throws Exception {
        logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage());
        return getExceptionTranslator().translate(new BadClientCredentialsException());
    }
    
    @ExceptionHandler(OAuth2Exception.class)
    public ResponseEntity<OAuth2Exception> handleException(OAuth2Exception e) throws Exception {
        logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage());
        return getExceptionTranslator().translate(e);
    }
    
    private WebResponseExceptionTranslator getExceptionTranslator() {
        return providerExceptionHandler;
    }
    
    }
    

    Change in web.xml : just replace the URL with new one

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/user/login</url-pattern>
    </servlet-mapping>
    

    And finally create bean with logincontroller class and change the URL in spring-security.xml.

    Change the oauth token url and url of clientCredentialsTokenEndpointFilter as mentioned below.

     <sec:http pattern="/user/login" create-session="stateless" authentication-manager-ref="clientAuthenticationManager" use-expressions="true" >
    
        <sec:intercept-url pattern="/user/login" access="isFullyAuthenticated()"/>
        <sec:csrf disabled="true"/>
        <sec:anonymous enabled="false" />
        <sec:http-basic entry-point-ref="clientAuthenticationEntryPoint" />
        <sec:custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
    </sec:http>
    <bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
        <constructor-arg value="/user/login"></constructor-arg>
        <property name="authenticationManager" ref="clientAuthenticationManager" />
          <property name="filterProcessesUrl" value="/user/login" />
    </bean>
    <bean class="com.oauth2.provider.endpoint.LoginContrller" />