1

I want to create a REST API endpoint, to let users change/update their credentials.

I have a CustomUserDetails:

@Entity
@Table(name="ACCOUNT_USER")
public class CustomUserDetails implements UserDetails {

    private static final long serialVersionUID = 1L;

    @Id
    @NotBlank
    @Column(name = "USERNAME", unique=true)
    private String username;

    @NotBlank
    @Column(name = "PASSWORD")
    private String password;

    @Column(name = "LOCKED")
    private boolean locked;

    @ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinTable(name = "USER_ROLE", 
                joinColumns = @JoinColumn(name = "USER_ID"), 
                inverseJoinColumns = @JoinColumn(name = "ROLE_ID"))
    private Set<CustomRole> roles;

    public CustomUserDetails(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<GrantedAuthority> authorities) {
        this.setUsername(username);
        this.setPassword(password);
        this.roles = new HashSet<CustomRole>();
        for (GrantedAuthority authority: authorities) {
            roles.add(new CustomRole(authority.getAuthority()));
        }
    }

    public CustomUserDetails() { // jpa only
    }
    //setters and getters
}

The Rest controller looks like this:

@SuppressWarnings("rawtypes")
@RequestMapping(value = "/singup", method = RequestMethod.PUT)
public ResponseEntity updateCredentials(@RequestBody CustomUserDetails user, HttpServletResponse response) throws IOException {

    logger.debug("Attempting credentials update " + user.getUsername());

    try {
        authenticateUserAndSetSession(user, response);
    } catch(BadCredentialsException ex) {
        return SecurityUtils.createCustomResponseEntity(CustomStatusCode.BAD_CREDENTIALS);
    }

    customUserDetailsService.update(user, newPassword); // where to add this newPassword ?

    return SecurityUtils.createCustomResponseEntity(CustomStatusCode.OK);
}

As you can see, there is nop newPassword field for the CustomUserDetails.

Does it make sense to add this field to this object ? What are other options ? What is the best practice in this case ?

Arian
  • 7,397
  • 21
  • 89
  • 177
  • I don't have an answer in terms of spring security, but storing the password in a string should be avoided for security reasons. It is better to use a char array or not have it in any objects at all. http://stackoverflow.com/questions/8881291/why-is-char-preferred-over-string-for-passwords – tima May 15 '17 at 02:21

1 Answers1

1

First, it does not make sense to add newPassword field in CustomUserDetails class.

Second, it is better and common to update only password in different endpoint (e.g. endpoint like /change_password, /update_password by POST method). But you can use PUT method of /signup endpoint to update CustomUserDetails field but ignore the password field for this case. It is better to not keep it straight forward.

Third, the best practice

  1. Separate endpoint for only update password
  2. This endpoint method is allowed for all logged in user.
  3. You should take two value one is oldPassword and another is newPassword
  4. Before update password of the logged in user hash the both old and new password (hashing algorithm must be same when you created user).
  5. After hashing if oldPassword matched with existing hashed password from DB then update the field with hashed newPassword
Zico
  • 2,349
  • 2
  • 22
  • 25