Spring Security Context Set Authentication object not working

authenticationsecurity-context-tokenspringspring-security

I have a scenario where I have to force the users to reset password on first login. For
this I am using a custom successAuthenticationHandler.
All this handler trying to do is see if logged in user requires to reset password. If yes create a new UsernamePasswordAuthenticationToken and set it onto SecurityContext. And then redirect to resetPasswordUrl.

Here is my onAuthenticationSuccess method:

@Override
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response,
        final Authentication authentication) throws ServletException, IOException {

    final AugmentedUser aUser = (AugmentedUser) SecurityContextHolder.getContext().getAuthentication()
            .getPrincipal();
    System.out.println("In password reset handler.");
    if (authorizationService.isPasswordResetRequired(aUser.getUsername(), aUser.getUsertype())) {
        LOG.debug("Password reset is required.");
        System.out.println("Password reset is required");
        final UsernamePasswordAuthenticationToken authRequest = reAssignUserWithOnlyResetPasswordRole(aUser,
                request);
        SecurityContextHolder.getContext().setAuthentication(authRequest);
        SecurityContextHolder.getContext().getAuthentication();
        System.out.println("User reassinged with only RESET_PASSWORD Authority, redirecting to resetPasswordPage");
        response.sendRedirect(resetPasswordUrl);

        //super.onAuthenticationSuccess(request, response, newAuthentication);
    } else {
        super.onAuthenticationSuccess(request, response, authentication);
    }

}

If yes create another UsernamePasswordAuthenticationToken with same credentials as logged in user, but just assign him a single role "RESET_PASSWORD", so that he cannot access anything alse by hitting any other link/url.

private UsernamePasswordAuthenticationToken reAssignUserWithOnlyResetPasswordRole(final AugmentedUser aUser,
        final HttpServletRequest request) {
    final String username = aUser.getUsername();
    final String password = aUser.getPassword();
    final boolean isEnabled = aUser.isEnabled();
    final boolean isAccountNonExpired = aUser.isAccountNonExpired();
    final boolean isCredentialsNonExpired = aUser.isCredentialsNonExpired();
    final boolean isAccountNonLocked = aUser.isAccountNonLocked();
    LOG.debug("Re-assigning the user: " + username + " with only RESET PASSWORD AUTHORITY");
    System.out.println("Re-assigning the user: " + username + "with only RESET PASSWORD AUTHORITY");
    final Map<String, String> userAttributesMap = new HashMap<String, String>();
    final AugmentedUser userWithResetPasswordRole = new AugmentedUser(username, password, aUser.getUsertype(),
            isEnabled, isAccountNonExpired, isCredentialsNonExpired, isAccountNonLocked,
            createResetPasswordGrantedAuhtority(), userAttributesMap);

    final UsernamePasswordAuthenticationToken authenticationRequest = new UsernamePasswordAuthenticationToken(
            userWithResetPasswordRole, userWithResetPasswordRole.getAuthorities());

    //WebAuthenticationDetails are required for sessionId and ipAddress
    final WebAuthenticationDetails webAuthenticationDetails = new WebAuthenticationDetails(request);
    authenticationRequest.setDetails(webAuthenticationDetails);

    return authenticationRequest;

}

Now I do see the new UsernamePasswordAuthenticationToken being created with just a RESET password role.
But looks like while doing redirect to the resetPasswordURl the spring filter do some checks and user is getting unauthenticated after I set my new UsernamePasswordAuthenticationToken .

Heres is the root cause that I see in the logs:

doAuthentication - Authentication attempt using
org.springframework.security.authentication.dao.DaoAuthenticationProvider
2012-02-15 22:47:20,931 [http-8081-6] DEBUG       org.springframework.security.web.access.ExceptionTranslationFilter    org.springframework.security.web.access.ExceptionTranslationFilter handleException - Authentication exception occurred; redirecting to authentication entry point

org.springframework.security.authentication.BadCredentialsException: Bad credentials
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:67)
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:139)
at org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:120)

Any comments where I am going wrong ?

Best Answer

It's difficult to say without more context from the log (the rest of the stacktrace plus the preceding log messages), but my best guess is that you're using the wrong constructor for UsernamePasswordAuthenticationToken. For historical reasons it takes Object arguments which doesn't help.

The two-arg version is supposed to take the username and credentials, and creates an unauthenticated token (for a request) which is not valid from the security interceptor's perspective. So I'm guessing the interceptor is trying to reauthenticate the token (should be obvious from the stacktrace where the call is coming from) and it is failing because the credentials parameter is actually a list of authorities rather than a password.

So use:

new UsernamePasswordAuthenticationToken(
        userWithResetPasswordRole, null, userWithResetPasswordRole.getAuthorities());

instead.

Also, you will need to have a custom voter which handles RESET_PASSWORD as it will not be recognised by a default configuration. Alternatively use the ROLE_ prefix, i.e. ROLE_RESET_PASSWORD.

Related Topic