0
votes

I am building an Application using PlayFramework / Scala.

For my security layer I am using Auth0 which is working fine for the main page, and I am already able to get profile information / add new users etc.

Now I have an API and I want to let people use it only when they are connected as well so I added this custom Action on my API controller :

  def AuthenticatedAction(f: Request[AnyContent] => Future[Result]): Action[AnyContent] = {
    Action.async { implicit request =>

      (request.session.get("idToken").flatMap { idToken =>
        cache.get[JsValue](idToken + "profile")
      } map { profile =>
        f(request) // IF USER CONNECTED THEN ADD PROFILE TO REQUEST AND PROCEED
      }).orElse {
        Some(Future(Redirect("/login"))) // OTHERWISE REDIRECT TO LOGIN PAGE
      }.get
    }
  }

I am able to use it for my read action (returning only one record by ID) :

def read(entityName: String, id: String) = AuthenticatedAction {
   // SOME NOT RELEVANT CODE     
}

My problem comes when I try to send json to create an object :

This was my code working before I tried to add a custom authenticated action :

def store(entityName: String, id: String) = Action.async(parse.json) {
      // SOME NOT RELEVANT CODE
}

Now I was expecting to use

def store(entityName: String, id: String) = AuthenticatedAction(parse.json) {
    // SOME NOT RELEVANT CODE
}

Here is the compile error :

  type mismatch; found : play.api.mvc.BodyParser[play.api.libs.json.JsValue]
  required: play.api.mvc.Request[play.api.mvc.AnyContent] ⇒ scala.concurrent.Future[play.api.mvc.Result]

And I know it comes from the fact that I do not support passing custom body parsers, I have looked into using ActionBuilder from the docs as well but I am trying to use the code provided by Auth0.

Is there a way to handle custom parsers when the custom action is not defined as a class ?

1

1 Answers

0
votes

So I just found a solution !

I noticed Action async has actually 2 different signatures :

async[A](bodyParser: BodyParser[A])(block: (R[A]) ⇒ Future[Result]): Action[A] 

and

 async(block: ⇒ Future[Result]): Action[AnyContent] 

So I just added another AuthenticationAction on my part

  def AuthenticatedAction[A](b: BodyParser[A])(f: Request[A] => Future[Result]): Action[A] = {
    Action.async(b) { implicit request =>

      (request.session.get("idToken").flatMap { idToken =>
        cache.get[JsValue](idToken + "profile")
      } map { profile =>
        f(request) // IF USER CONNECTED THEN ADD PROFILE TO REQUEST AND PROCEED
      }).orElse {
        Some(Future(Redirect("/login")))
      }.get
    }
  }