Game Limit 2

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.

+6
source share
2 answers

What you need to understand is that the form is immutable. But there is an easy-to-use utility method for creating a new form with added errors:

 loginForm.copy(errors = Seq(FormError("email", "Already registered"))) 
+3
source

Of course, combining authentication with verification simply does a simple set of operations. Below is unchecked, but this is the direction in which I would include the right projections, passed through understanding.

 // point of validation is to sanitize inputs last I checked val form = Form(tuple("email"โ†’ email, "password"โ†’ nonEmptyText) val res = for{ case(e,p) <- form.bindFromRequest.toRight("Invalid email or password") success <- User.authenticateAwait(e,p).right } yield success res fold( Conflict(Left(_)), u=> Ok(Right(u)) ) 
0
source

Source: https://habr.com/ru/post/926386/


All Articles