I am trying to authenticate users through a remote authentication service. I wrote a helper method for sending a message for maintenance and waiting for the result:
def authenticateAwait(email: String, password: String ): Either[String, Option[User]] = { try { val future = authenticate(email, password) Right(Await.result(future, timeout.duration)) } catch { case _ โ Left("Unable to connect to authentication server") } }
It returns a Left[String] with a description of the error if the message cannot be sent or there is no response. If a service response is received, it returns Right[Option[User]] . The service responds with Option[User] depending on the authentication result.
To perform the actual authentication, I created the form using several validators, here it is:
val loginForm = Form( tuple( "email" โ email, "password" โ nonEmptyText ) verifying ("Invalid email or password", result => result match { case (email, password) โ User.authenticateAwait(email, password) match { case Left(_) โ true case Right(optUser) โ optUser.isDefined } }) verifying ("Unable to connect to authentication server", result => result match { case (email, password) โ User.authenticateAwait(email, password) match { case Left(_) โ false case Right(optUser) โ true } }) )
One thing bothers me, it calls authenticateAwait twice. This means that exactly two messages will be sent in one check. What I really need is to call authenticateAwait once, save the result and perform various checks on it. There seems to be no easy solution.
Authentication requires access to the form fields, which means that the form must be bound and then verified, but there is no way to attach errors to an existing form (am I wrong?).
Errors can only be attached to the form at the time of its creation, so I have to authenticate with the validators, but then the above problem arises.
The workaround I came with is to define a method and var inside it.
def loginForm = { var authResponse: Either[String, Option[commons.User]] = null Form( tuple( "email" โ email, "password" โ nonEmptyText ) verifying ("Invalid email or password", result โ result match { case (email, password) โ authResponse = User.authenticateAwait(email, password) authResponse match { case Left(_) โ true case Right(optUser) โ optUser.isDefined } }) verifying ("Unable to connect to authentication server", result โ result match { case (email, password) โ authResponse match { case Left(_) โ false case Right(optUser) โ true } }) ) }
This is clearly a hack. Are there any better solutions?
Update: In my opinion, the form should only sanitize the input, but authentication should be performed later outside the form. The problem is that errors are sent to the view as part of the Form , and it is not possible to attach errors to an existing form. There is no easy way to create a new form with errors.