Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spring-oauth2 login success handler

Is there a way to add a Login Success Handler using spring-oauth2?

I tried using Basic Authentication Filter but it only filters the client credentials not the user credentials.

Or do I need to create a custom user authentication manager?

TIA

like image 968
Franzis Kaner Avatar asked Mar 30 '15 05:03

Franzis Kaner


1 Answers

This solution will work for password flow and for others I am not sure. You can add your custom filter in position of "before=BASIC_AUTH_FILTER" in http tag which is in oauth-server config, and you can achieve by parse response of "oauth/token" so create ByteArrayResponseWrapper to get response, Here I am using TeeOutputStream class from "org.apache.commons commons-io",

private class ByteArrayResponseWrapper extends HttpServletResponseWrapper {

    public ByteArrayResponseWrapper(ServletResponse response) {
        super((HttpServletResponse) response);
    }

    private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return new DelegatingServletOutputStream(new TeeOutputStream(
                super.getOutputStream(), byteArrayOutputStream));
    }

    public byte[] getByteArray() {
        return this.byteArrayOutputStream.toByteArray();
    }
}

and I have created token extractor to separate the code of extracting access_token

public class OAuth2AccessTokenExtractor implements
    OAuth2AccessTokenExtractor {

private ObjectMapper mapper = new ObjectMapper();

public String getAccessTokenValue(byte[] response) {
    try {
        return mapper.readValue(response, OAuth2AccessToken.class)
                .getValue();
    } catch (JsonParseException e) {
        e.printStackTrace();
    } catch (JsonMappingException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
 }

}

after create your filter override doFilter like this

private DefaultTokenServices tokenServices;

private OAuth2AccessTokenExtractor tokenExtractor;

@Override
public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {

    // create wrapper to read response body
    ByteArrayResponseWrapper responseWraper = new ByteArrayResponseWrapper(
            response);

    // led them go
    chain.doFilter(request, responseWraper);

    // get ClientAuthentication
    Authentication clientAuthentication = SecurityContextHolder
            .getContext().getAuthentication();

    // is authenticated or not to proceed
    if (clientAuthentication != null
            && clientAuthentication.isAuthenticated()) {

        // callBack client authenticated successfully
        onSuccessfulClientAuthentication(request, response,
                clientAuthentication);

        // check response status is success of failure
        if (responseWraper.getStatus() == 200) {

            // extract accessToken from response
            String token = tokenExtractor
                    .getAccessTokenValue(responseWraper.getByteArray());

            if (token != null && !token.isEmpty()) {

                // load authentication from token
                OAuth2Authentication oAuth2Authentication = this.tokenServices
                        .loadAuthentication(token);
                OAuth2AccessToken actualAccessToken = this.tokenServices
                        .getAccessToken(oAuth2Authentication);

                // callBack user authenticated successfully
                onSuccessfulUserAuthentication(request, response,
                        clientAuthentication, oAuth2Authentication,
                        actualAccessToken);
            } else {
                log.error("access token is empty from extractor");
            }
        } else {
            // callBack user authenticated failure
            onFailureUserAuthentication(request, response,
                    clientAuthentication, request.getParameter("username"));
        }
    } else {
        // callBack client authenticated failure
        onFailClientAuthentication(request, response,
                request.getParameter(OAuth2Utils.CLIENT_ID));
    }
}

protected void onSuccessfulClientAuthentication(ServletRequest request,
        ServletResponse response, Authentication authentication) {
}

protected void onFailClientAuthentication(ServletRequest request,
        ServletResponse response, String clientId) {
}

protected void onSuccessfulUserAuthentication(ServletRequest request,
        ServletResponse response, Authentication clientAuthentication,
        OAuth2Authentication userOAuth2Authentication,
        OAuth2AccessToken token) {
}

protected void onFailureUserAuthentication(ServletRequest request,
        ServletResponse response, Authentication clientAuthentication,
        String username) {
}

while create filter instance inject tokenServices. now onSuccessfulClientAuthentication, onFailClientAuthentication, onSuccessfulUserAuthentication and onFailureUserAuthentication will be called according to your authentication

for more you can refer this code on github

Edited:

The above snippet works fine when you have default token response and it is just uses ServletResponseWrapper and extracting. But still it seems like vulnerable so you can get to know the user authentication success by through org.springframework.security.oauth2.provider.token.TokenEnhancer class

Follow this answer for details.

like image 73
Naveen raj Avatar answered Nov 19 '22 21:11

Naveen raj