brian661
brian661

Reputation: 548

Spring security implementation with AWS congnito authentication on front end

I separate my application into 2 parts:

My flow of authentication

  1. User redirected to congnito and login. congnito will return a unique id and JWT.
  2. Front end passes the unique id and JWT to back end controller.
  3. backend validate JWT and return user information from DB

My question is:

// controller
public String login(HttpServletRequest req, String cognitoId, String jwt) { 
    // check JWT with AWS
    if(!AwsJwtChecker(cognitoId, jwt))
        return createErrorResponseJson("invalid jwt");

    UsernamePasswordAuthenticationToken authReq
      = new UsernamePasswordAuthenticationToken(cognitoId, "");
    Authentication auth = authManager.authenticate(authReq);

    SecurityContext sc = SecurityContextHolder.getContext();
    sc.setAuthentication(auth);
    HttpSession session = req.getSession(true);
    session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, sc);

    MyUser user = userRepository.selectUserByCognitoId(cognitoId);
    return createLoginSuccessResponse(user);
}

// web config
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
      String cognitoId = authentication.getName();

      // check user exist in db or not
      MyUser user = userRepository.selectUserByCognitoId(cognitoId);
      if (user != null) {
            return new UsernamePasswordAuthenticationToken(username, "", user.getRoles());
       } else {
            throw new BadCredentialsException("Authentication failed");
       }
    }
    @Override
    public boolean supports(Class<?>aClass) {
        return aClass.equals(UsernamePasswordAuthenticationToken.class);
    }
}

Upvotes: 1

Views: 757

Answers (1)

Chris
Chris

Reputation: 5633

Is this a bad practice to authenticate on front end and pass data to back end for spring security? If so, may I have any suggestion to change my implementation flow?

No, in fact it's best practice. JWT is exactly for that purpose: You can store information about the user and because of the signature of the token, you can be certain, that the information is trustworthy.

You don't describe what you are saving in the database, but from my perspective, you are mixing two authentication methods. While it's not forbidden, it might be unnecessary. Have you analysed your token with jwt.io? There are many information about the user within the token and more can be added.

Cognito is limited in some ways, like number of groups, but for a basic application it might be enough. It has a great API to manage users from within your application, like adding groups or settings properties.

You don't describe what you do with the information that is returned with 3). Vue can too use the information stored in the jwt to display a username or something like that. You can decode the token with the jwt-decode library, eg, and get an object with all information.

To call AuthenticationProvider.authenticate...

Having said that, my answer to your second question is: You don't need the whole authentication part in you login method.

// controller
public String login(HttpServletRequest req, String cognitoId, String jwt) { 
    // check JWT with AWS
    if(!AwsJwtChecker(cognitoId, jwt))
        return createErrorResponseJson("invalid jwt");

    return userRepository.selectUserByCognitoId(cognitoId);
}

This should be completely enough, since you already validate the token. No need to authenticate the user again. When spring security is set up correctly, the jwt will be set in the SecurityContext automatically.

The problem I see with your implementation is that anyone could send a valid jwt and a random cognitoId and receive user information from the database. So it would be better to parse the jwt and use something from within the jwt, like username, as identifier in the database. The token can't be manipulated, otherwise the validation fails.

public String login(String jwt) { 
    // check JWT with AWS
    if(!AwsJwtChecker(jwt))
        return createErrorResponseJson("invalid jwt");

    String identifier = getIdentifier(jwt);

    return userRepository.selectUserByIdentifier(identifier);
}

Upvotes: 1

Related Questions