Search code examples
scalapostgresql-9.2playframework-2.2anorm

Password and password hash in a model and in db table


I have a table User(id, password_hash, ....) in db and a model for it:

case class User(
  id: Pk[Long] = NotAssigned,
  email: String,
  password: Option[String] = None,
  passwordHash: Option[String] = None
)


object User {

  def create(newUser: User): Option[Long] = //.....

  //on("password_hash" -> generatePasswordHash(newUser.password)

  def generatePasswordHash(p: String) = //....
 }

The point is Password field exists only the model User and is filled up only I register a new user:

val newUser = User(email = emailFromForm, password = Some(passwordFromForm))

I send to db only a hash of the password. Obviously, when I retrieve it from db, Password field in None but PasswordHash has a value.

I made Password and PasswordHash to be Option because I think they should be Options, shouldn't they? I'm not sure, though, whether it's right or wrong.

The question is my is this a good approach?


Solution

  • Why do you want to have User.password at all?

    case class User(
      id: Pk[Long] = NotAssigned,
      email: String,
      passwordHash: String 
    )
    
    object User {
      // or maybe Option[User] or Try[User]
      def create(email: String, password: String): Option[Long] = {
        val passwordHash = hashPassword(hash)
        val newUser = User(email, passwordHash)
        // save newUser to DB
      }
    
      // you may want to distinguish between "no such email" and "wrong password"
      // in which case you'd have something like Either[PasswordCheckFailure, User]
      def checkPassword(email: String, password: String): Option[User] = {
        val possibleUser: Option[User] = // get user by email
        possibleUser.filter(_.passwordHash = hashPassword(password))
      }
    
      private def hashPassword(password: String): String = ...
    }
    

    You may want to have a salt as well, see e.g. https://crackstation.net/hashing-security.htm. In this case you either store it in the same field as password or add another field:

    case class User(
      id: Pk[Long] = NotAssigned,
      email: String,
      passwordHash: String,
      passwordSalt: String = // generate random string
    )