0

I'm trying to implement an authentication mechanism similar to this example:

    def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
      val maybeToken = requestHeader.headers.get("X-SECRET-TOKEN")
      maybeToken map { token =>
        action(token)(requestHeader) // apply requestHeader to EssentialAction produces the Iteratee[Array[Byte], SimpleResult]
      } getOrElse {
        Done(Unauthorized("401 No Security Token\n")) // 'Done' means the Iteratee has completed its computations
      }
    }

However, in my case I'm mapping a random token value to a session on the server side stored in Mongodb. The goal was to be able to let a user terminate all his other sessions at will.

However, the data I get from ReactiveMongo is going to be wrapped in a Future.

I would like something like this:

    def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
      val maybeToken = requestHeader.headers.get("session")
      maybeToken map { token =>
        //This returns a future..
        Session.find(session).map { result => 
          result match
            case Some(session) => action(session)(requestHeader)
            case None => Done(Unauthorized())
        }
      } getOrElse {
        Done(Unauthorized("401 No Security Token\n")) // 'Done' means the Iteratee has completed its computations
      }
    }

Is this possible with EssentialAction?

James Cowhen
  • 2,837
  • 3
  • 24
  • 32

2 Answers2

2

Iteratee.flatten goes from Future[Iteratee[A, E]] => Iteratee[A, E] so you could do it like this:

def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
   val maybeToken = requestHeader.headers.get("session")

   val futureIteratee: Future[Iteratee[Array[Byte], SimpleResult]] = maybeToken map { token =>
     //This returns a future..
     Session.find(token).map {
       case Some(session) => action(session)(requestHeader)
       case None => Done[Array[Byte], SimpleResult](Unauthorized("Invalid token"))
     }
   } getOrElse {
     Future.successful(Done[Array[Byte], SimpleResult](Unauthorized("401 No Security Token\n")))
   }

   Iteratee.flatten(futureIteratee)
}
johanandren
  • 11,249
  • 1
  • 25
  • 30
  • I get a `found : scala.concurrent.Future[play.api.libs.streams.Accumulator[akka.util.ByteString,play.api.mvc.Result]]`, `required: scala.concurrent.Future[play.api.libs.iteratee.Iteratee[?,?]]`... probably I am missing something (not having this quite right)! – Andy Hayden Sep 05 '17 at 21:47
1

You can use an ActionBuilder as the invokeBlock method return a Future[SimpleResult] so you can flatMap your future into a call to the underlying block

Something like

object Authenticated extends ActionBuilder[AuthenticatedRequest] {
  def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
    Session.find(session).map { result => 
      result match
        case Some(session) => block(new AuthenticatedRequest(session))
        case None => Unauthorized()
    }        
}

}

where AuthenticatedRequest is your request type that wraps your session object

Seb Cesbron
  • 3,823
  • 15
  • 18
  • Note that there is an important difference between Action and EssentialAction, namely that Action will always parse the request body while EssentialAction allows you to bail out after receiving the request headers ignoring the body. This might be important to protect against DoS attacs for example. – johanandren Feb 19 '14 at 11:52