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 ?
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
}
}
Related
I am trying to add authentication to my Play Framework single page app.
What I would like to have is something like:
def unsecured = Action {
Ok("This action is not secured")
}
def secured = AuthorizedAction {
// get the authenticated user's ID somehow
Ok("This action is secured")
}
For a traditional web app, I had previously done this, following Play Framework docs:
def authenticate = Action { implicit request =>
loginForm.bindFromRequest.fold(
formWithErrors => BadRequest(views.html.login(formWithErrors)),
user => {
Redirect(routes.Application.home).withSession(Security.username -> user._1)
}
)
}
def logout = Action {
Redirect(routes.Auth.login).withNewSession.flashing(
"success" -> "You are now logged out."
)
}
and the Authorized Action is extending ActionBuilder as follows:
object AuthorizedAction extends ActionBuilder[Request] with Results {
/**
* on auth success: proceed with the request
* on auth failure: redirect to login page with flash
*/
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
// TODO: is "isDefined" enough to determine that user is logged in?
if(request.session.get("username").isDefined) {
block(request)
}
else {
Future.successful(Redirect(routes.Auth.login).flashing(
"failure" -> "You must be logged in to access this page."
))
}
}
}
For single page applications however, this approach doesn't exactly work anymore.
This article by James Ward explains how the new approach is to be designed, and includes a Java implementation:
Securing SPA and rest services
The implementation was redone in Scala by Marius Soutier: Securing SPA in Scala
In his example, he implements a Security trait:
trait Security { self: Controller =>
val cache: CacheApi
val AuthTokenHeader = "X-XSRF-TOKEN"
val AuthTokenCookieKey = "XSRF-TOKEN"
val AuthTokenUrlKey = "auth"
/** Checks that a token is either in the header or in the query string */
def HasToken[A](p: BodyParser[A] = parse.anyContent)(f: String => Long => Request[A] => Result): Action[A] =
Action(p) { implicit request =>
val maybeToken = request.headers.get(AuthTokenHeader).orElse(request.getQueryString(AuthTokenUrlKey))
maybeToken flatMap { token =>
cache.get[Long](token) map { userid =>
f(token)(userid)(request)
}
} getOrElse Unauthorized(Json.obj("err" -> "No Token"))
}
}
Functions are now secured like this instead of a plain Action:
def ping() = HasToken() { token => userId => implicit request =>
user.findByID (userId) map { user =>
Ok(Json.obj("userId" -> userId)).withToken(token -> userId)
} getOrElse NotFound (Json.obj("err" -> "User Not Found"))
}
where .withToken is defined as:
implicit class ResultWithToken(result: Result) {
def withToken(token: (String, Long)): Result = {
cache.set(token._1, token._2, CacheExpiration)
result.withCookies(Cookie(AuthTokenCookieKey, token._1, None, httpOnly = false))
}
def discardingToken(token: String): Result = {
cache.remove(token)
result.discardingCookies(DiscardingCookie(name = AuthTokenCookieKey))
}
}
I am not liking how complex the "ping" function above has become, and would have preferred to use an Action Builder (like the first example), where auth failure is caught and dealt with at a single point. (as of now, if I want to secure functions ping2 and ping3, each one has to check whether the user is found and deal with the "not found" case)
I have tried to put together an action builder, inspired by Marius' implementation, most particularly his use of the cacheApi which is necessary.
However the AuthorizedAction is an object, and cacheApi needs to be injected (so need to change the object to singleton class), or cannot be declared in an object without being defined.
I also feel like the AuthorizedAction needs to remain an object, in order to be used as:
def secured = AuthorizedAction {
Would anyone please clear up the confusion, and possibly help with some implementation details?
Thanks a lot
The simplest way in my opinion is to go with ActionBuilder. You can define an action builder as a class (and pass it some dependencies) or as an object.
First you'll need to define a type a request that will contain the information about the user:
// You can add other useful information here
case class AuthorizedRequest[A](request: Request[A], user: User) extends WrappedRequest(request)
Now define your ActionBuilder
class AuthorizedAction(userService: UserService) extends ActionBuilder[AuthorizedRequest] {
override def invokeBlock[A](request: Request[A], block: (AuthorizedRequest[A]) ⇒ Future[Result]): Future[Result] = {
request.headers.get(AuthTokenHeader).orElse(request.getQueryString(AuthTokenUrlKey)) match {
case Some(token) => userService.findByToken(token).map {
case Some(user) =>
val req = AuthorizedRequest(request, user)
block(req)
case None => Future.successful(Results.Unauthorized)
}
case None => Future.successful(Results.Unauthorized)
}
}
}
Now you can use it in your controller:
val authorizedAction = new AuthorizedAction(userService)
def ping = authorizedAction { request =>
Ok(Json.obj("userId" -> request.user.id))
}
I am using play framework 2.5. In my application i want to intercept each request to check whether any user exists or not in session of that request. if user does not exists then request redirect to the login page. So, my question is how can i achieve it in my application ?
Thank You very much in advance.
After you check for the appropriate user
Redirect(routes.Application.login())
I won't assume anything about your authentication strategy, but he're is a simple token-based authentication strategy I use when I want to intercept the user on each request.
object UserAuthenticator extends Controller {
case class UserAuthenticatedRequest[A](user: User, authToken: String, request: Request[A]) extends WrappedRequest(request)
def userAuthenticated[A](p: BodyParser[A])(f: UserAuthenticatedRequest[A] => Result) = {
Action(p) { implicit request =>
request.headers.get(AUTHORIZATION).map(_.split(" ")) match {
case Some(Array(k, v)) if k == "auth-token" => findUserByAuthToken(v).map(user => f(UserAuthenticatedRequest(user, v, request))).getOrElse(Unauthorized)
case _ => Unauthorized
}
}
}
def userAuthenticated(f: UserAuthenticatedRequest[AnyContent] => Result): Action[AnyContent] = {
userAuthenticated(parse.anyContent)(f)
}
Using the above, my controllers can do the following:
def myControllerMethod = userAuthenticated { request =>
doSomethingWith(request.user)
}
You'll notice in my case I return Unauthorized, but you could easily replace that with the redirect you're looking for.
I am working on writing a web application that takes multipart files as input and uploads them to an S3 instance. Since some of the files can be very large, I am using a custom Body Parser to send the chunks to S3 as they come in.
I want to do validation on the request prior to uploading the file (to check that the user has permission/enough space, etc). From reading the Play docs, it seems like extending ActionBuilder is the right way to do this. I noticed in SecureSocial, there is a SecureActionBuilder and I believe I should extend this in order to build a secure action (which is what I want).
I tried this simple test to see if I could print out the userId, therefore being able to perform actions based on the user.
object FileValidationAction extends SecuredActionBuilder {
def invokeBlock[A](request: SecuredRequest[A], block: SecuredRequest[A] => Future[SimpleResult]) = {
Logger.info("User id is " + request.user.userProfile.userId)
block(request)
}
}
However, the method never got called.
Next, I tried overriding the method from the SecuredActionBuilder object:
object FileValidationAction extends SecuredActionBuilder {
override def invokeBlock[A](request: Request[A], block: SecuredRequest[A] => Future[SimpleResult]) = {
val securedResult: Option[SecuredRequest[A]] = request match {
case r: SecuredRequest[A] => Option(r)
case _ => None
}
Logger.info("Calling action ------- WOO!")
securedResult match {
case Some(r) =>
block(r)
case _ =>
Future.successful(Forbidden)
}
}
}
That method gets called but the request coming in is not a SecuredRequest as I was hoping.
How do I build a SecuredAction, using a custom body parser, that I can do validation on before it completes (or even starts) the upload?
EDIT:
To clarify, I will be calling the Action with the following method signature:
def upload = FileValidationAction(streamingBodyParser(streamConstructor)) { request =>
The problem is that you are not invoking the original code in SecuredActionBuilder that actually checks if the user is there and constructs the SecuredRequest instance.
Something like this should work:
// A sample usage of the action
def checkFile = FileValidationAction { request =>
Ok("")
}
// The builder implementation
object FileValidationAction extends FileValidationActionBuilder {
def apply[A]() = new FileValidationActionBuilder()
}
class FileValidationActionBuilder(authorize: Option[Authorization[DemoUser]] = None) extends SecuredActionBuilder(authorize) {
def validateFile[A](block: SecuredRequest[A] => Future[SimpleResult]): SecuredRequest[A] => Future[SimpleResult] = { securedRequest =>
Logger.info(s"User id is ${securedRequest.user.main.userId}")
block(securedRequest)
}
override def invokeBlock[A](request: Request[A], block: (SecuredRequest[A]) => Future[SimpleResult]): Future[SimpleResult] = {
invokeSecuredBlock(authorize, request, validateFile(block))
}
}
You would need to add this inside the controller where you plan to use this actions. If you need to use it in multiple controllers then create a trait that you can extend with this.
Also note that in this sample code I am using the DemoUser type I have in the samples. You will need to change that to the type you are using in your app to represent users.
I am using Play's ActionBuilder to create various Actions that secure my controllers. For instance, I implemented IsAuthenticated to make sure that certain actions can only be accessed if the user would be logged in:
case class AuthRequest[A](user: String, request: Request[A]) extends WrappedRequest[A](request)
private[controllers] object IsAuthenticated extends ActionBuilder[AuthRequest] {
def invokeBlock[A](req: Request[A], block: (AuthRequest[A]) => Future[SimpleResult]) = {
req.session.get("user").map { user =>
block(new AuthRequest(user, req))
} getOrElse {
Future.successful(Results.Unauthorized("401 No user\n"))
}
}
}
Using IsAuthenticated I can (1) restrict an action to users who are logged in, and (b) access the user being logged in:
def auth = IsAuthenticated { implicit authRequest =>
val user = authRequest.user
Ok(user)
}
Furthermore, I use ActionBuilder HasToken to ensure that an action was invoked with a token being present in the request's header (and, I can access the token value):
case class TokenRequest[A](token: String, request: Request[A]) extends WrappedRequest[A](request)
private[controllers] object HasToken extends ActionBuilder[TokenRequest] {
def invokeBlock[A](request: Request[A], block: (TokenRequest[A]) => Future[SimpleResult]) = {
request.headers.get("X-TOKEN") map { token =>
block(TokenRequest(token, request))
} getOrElse {
Future.successful(Results.Unauthorized("401 No Security Token\n"))
}
}
}
That way, I can make sure that an action was called with that token present:
def token = HasToken { implicit tokeRequest =>
val token = tokeRequest.token
Ok(token)
}
So far, so good...
But, how could I wrap (or, nest / compose) such actions as those defined above? For instance, I would like to ensure (a) that a user would be logged in and (b) that the token would be present:
def tokenAndAuth = HasToken { implicit tokeRequest =>
IsAuthenticated { implicit authRequest =>
val token = tokeRequest.token
val user = authRequest.user
}
}
However, the above action does not compile. I tried many different implementations but always failed to achieve the desired behavior.
In general terms: How could I compose Actions defined using Play's ActionBuilder in arbitrary order? In the above example, it would not matter if I would wrap IsAuthenticated in HasToken or the other way around -- the effect would be the same: the user would have to be logged in and would have to present the token.
Note: I have created a Gist that provides the complete source code.
ActionBuilder
ActionBuilders are not made for ad-hoc composition, but rather to build a hierarchy of actions so you end up using only a couple of actions throughout your controllers.
So in your example you should build IsAuthenticated on top of HasToken as I illustrated here.
This is a viable solution and can actually simplify your code. How often do you really need to compose on the spot?
EssentialAction
Ad-hoc composition could be achieved with EssentialActions (simply because they haven't changed from 2.1), but they have a few downsides, as Johan pointed out. Their API is not really intended for ad-hoc use either, and Iteratees are too low-level and too cumbersome for controller actions.
Actions
So finally your last option would be to write Actions directly. Actions do not support passing a WrappedRequest by default (that's why ActionBuilder exists). However you can still pass a WrappedRequest and have the next Action deal with it.
The following is the best I have come up with so far and is rather fragile I guess.
case class HasToken[A](action: Action[A]) extends Action[A] {
def apply(request: Request[A]): Future[SimpleResult] = {
request.headers.get("X-TOKEN") map { token =>
action(TokenRequest(token, request))
} getOrElse {
Future.successful(Results.Unauthorized("401 No Security Token\n"))
}
}
lazy val parser = action.parser
}
case class IsAuthenticated[A](action: Action[A]) extends Action[A] {
def apply(request: Request[A]): Future[SimpleResult] = {
request.session.get("user").map { user =>
action(new AuthRequest(user, request))
} getOrElse {
Future.successful(Results.Unauthorized("401 No user\n"))
}
}
lazy val parser = action.parser
}
object ActionComposition extends Controller {
def myAction = HasToken {
Action.async(parse.empty) { case TokenRequest(token, request) =>
Future {
Ok(token)
}
}
}
def myOtherAction = IsAuthenticated {
Action(parse.json) { case AuthRequest(user, request) =>
Ok
}
}
def both = HasToken {
IsAuthenticated {
Action(parse.empty) { case AuthRequest(user, request: TokenRequest[_]) =>
Ok(request.token)
}
}
}
}
Results
You can also compose at the Result level and only use the built-in actions. This is especially useful when trying to factor out error handling and other repetitive stuff. I have an example here.
Conclusion
We are still missing the capabilities that Play 2.1's action composition offered. So far to me it seems that ActionBuilder + Result composition is the winner as its successor.
The output from an action builder is an action, an action is essentially a function from request => future result, so you can actually just call it like this:
def tokenAndAuth = HasToken.async { implicit tokenRequest =>
IsAuthenticated { implicit authRequest =>
val token = tokenRequest
val user = authRequest.user
Ok("woho!")
}(tokenRequest) // <-- call the inner action yourself, returns Future[SimpleResult]
}
There may be problems if there is a body that you want to parse though, I think it will be parsed by the body parser of the outer request but I'm not sure what the inner one will do if you would specify a parser.
The reason why you cannot do this in a simpler way is that you do not only want to compose logic but also pass data into each action, if you had actions that would just bail on missing auth or token you could absolutely compose them as the play docs on action composition describes.
Side note: since you are only looking at headers and not accessing the body in either of your custom action decorators it might be a better idea to look into EssentialAction:s as those would let you reject the request without first parsing the body when auth or token is missing.
I am trying to create a custom play.api.mvc.Action which can be used to populate a CustomerAccount based on the request and pass the CustomerAccount into the controller.
Following the documentation for Play 2.2.x I've created an Action and ActionBuilder but I cannot seem to return the CustomerAccount from within the action.
My current code is:
case class AccountWrappedRequest[A](account: CustomerAccount, request: Request[A]) extends WrappedRequest[A](request)
case class Account[A](action: Action[A]) extends Action[A] {
lazy val parser = action.parser
def apply(request: Request[A]): Future[SimpleResult] = {
AccountService.getBySubdomain(request.host).map { account =>
// Do something to return the account like return a new AccountWrappedRequest?
action(AccountWrappedRequest(account, request))
} getOrElse {
Future.successful(NotFound(views.html.account_not_found()))
}
}
}
object AccountAction extends ActionBuilder[AccountWrappedRequest] {
def invokeBlock[A](request: Request[A], block: (AccountWrappedRequest[A]) => Future[SimpleResult]) = {
// Or here to pass it to the next request?
block(request) // block(AccountWrappedRequest(account??, request))
}
override def composeAction[A](action: Action[A]) = Account(action)
}
Note: This will not compile because the block(request) function is expecting a type of AccountWrappedRequest which I cannot populate. It will compile when using a straight Request
Additionally...
Ultimately I want to be able to combine this Account action with an Authentication action so that the CustomerAccount can be passed into the Authentication action and user authentication can be provided based on that customer's account. I would then want to pass the customer account and user into the controller.
For example:
Account(Authenticated(Action))) { request => request.account; request.user ... } or better yet as individual objects not requiring a custom request object.
I'm not sure if this is the best way to do it but I have managed to come up with a solution that seems to work pretty well.
The key was to match on the request converting it into an AccountWrappedRequest inside invokeBlock before passing it on to the next request. If another Action in the chain is expecting a value from an earlier action in the chain you can then similarly match the request converting it into the type you need to access the request parameters.
Updating the example from the original question:
case class AccountWrappedRequest[A](account: CustomerAccount, request: Request[A]) extends WrappedRequest[A](request)
case class Account[A](action: Action[A]) extends Action[A] {
lazy val parser = action.parser
def apply(request: Request[A]): Future[SimpleResult] = {
AccountService.getBySubdomain(request.host).map { account =>
action(AccountWrappedRequest(account, request))
} getOrElse {
Future.successful(NotFound(views.html.account_not_found()))
}
}
}
object AccountAction extends ActionBuilder[AccountWrappedRequest] {
def invokeBlock[A](request: Request[A], block: (AccountWrappedRequest[A]) => Future[SimpleResult]) = {
request match {
case req: AccountRequest[A] => block(req)
case _ => Future.successful(BadRequest("400 Invalid Request"))
}
}
override def composeAction[A](action: Action[A]) = Account(action)
}
Then inside the apply() method of another Action (the Authenticated action in my case) you can similarly do:
def apply(request: Request[A]): Future[SimpleResult] = {
request match {
case req: AccountRequest[A] => {
// Do something that requires req.account
val user = User(1, "New User")
action(AuthenticatedWrappedRequest(req.account, user, request))
}
case _ => Future.successful(BadRequest("400 Invalid Request"))
}
}
And you can chain the actions together in the ActionBuilder
override def composeAction[A](action: Action[A]) = Account(Authenticated(action))
If AuthenticatedWrappedRequest is then passed into the controller you would have access to request.account, request.user and all the usual request parameters.
As you can see there are a couple of cases where the response is unknown which would generate a BadRequest. In reality these should never get called as far as I can tell but they are in there just incase.
I would love to have some feedback on this solution as I'm still fairly new to Scala and I'm not sure if there might be a better way to do it with the same result but I hope this is of use to someone too.
I wrote a standalone small (ish) example that does what you're looking for:
https://github.com/aellerton/play-login-example
I gave up trying to use the Security classes that exist in the play framework proper. I'm sure they're good, but I just couldn't understand them.
Brief guide...
In the example code, a controller is declared as using the AuthenticatedRequests trait:
object UserSpecificController extends Controller with AuthenticatedRequests {
...
}
Forcing any page to require authentication (or redirect to get it) is done with the RequireAuthentication action:
def authenticatedIndex = RequireAuthentication { implicit request: AuthenticatedRequest[AnyContent] =>
Ok("This content will be accessible only after logging in)
}
Signing out is done by using the AbandonAuthentication action:
def signOut = AbandonAuthentication { implicit request =>
Ok("You're logged out.").withNewSession
}
Note that for this to work, you must override methods from the AuthenticatedRequests trait, e.g.:
override def authenticationRequired[A](request: Request[A]): Future[SimpleResult] = {
Future.successful(
Redirect(routes.LoginController.showLoginForm).withSession("goto" -> request.path)
)
}
There's more to it; best to see the code.
HTH
Andrew