My play application has an endpoint for receiving webhooks from Stripe.
In order to verify the webhooks, the request body needs to be compared against a signature and a signing key. This requires that I have access to the raw request body, as sent.
However, it seems that Play alters the request body, and I can't get access to the raw contents. This causes the computed signature to change, and the verification fails. More info: https://stackoverflow.com/a/43894244/49153
Here's my code:
#Singleton
class WebhookController #Inject()(cc : ControllerComponents,
env: Env)
(implicit ec: ExecutionContext)
extends AbstractController(cc) {
private val log = Logger("WebhookController")
def index(): Action[AnyContent] = Action.async { implicit req =>
val signature =
req.headers.headers.find(_._1 == "Stripe-Signature").map(_._2).getOrElse("").trim
if (verifySignature(req.body.toString, signature, env.webhookSecretKey))
Future.successful(ok("ok"))
else
Future.successful(ok("Couldn't verify signature"))
}
}
Here I'm trying to access the body using req.body.toString but it looks to be the deserialized json rather than the raw body.
Using req.body.asRaw returns a none.
Any ideas?
Solved this by using Action.async(parse.raw) and then doing req.body.asBytes().map(_.utf8String).getOrElse("") to obtain the raw string of the body. Some more info: https://www.playframework.com/documentation/2.7.x/ScalaBodyParsers
Related
I am trying to write a Scala Play Framework action that will verify a HmacSHA256 signature on an incoming POST request containing form-url-encoded data.
This does not seem straightforward in the Play framework because: i) actions builders only have access to headers, but do not have access to the request body, and ii) in order to calculate the signature we have to treat the request body as Array[ByteString], but when we come to process the form data we have to treat it as Map[String, Seq[String]], the problem being thatPlay forces us to choose a single type for our request, and we cannot easily "cast" the request body to a different type.
The only solution I have been able to come up with is to use an ActionRefiner that returns a WrappedRequest that embeds a callback to validate the signature. The callback in turn reparses the data using FormUrlEncodedParser.parse(new String(request.body.toArray)). This approach is illustrated in the code below.
This all seems overly convoluted. Is there a simpler way to verify Hmac signatures in Play, or am I simply running up against limitations of the API?
package actions
import akka.util.ByteString
import com.google.inject.Inject
import play.api.Logging
import play.api.mvc.Results.Unauthorized
import play.api.mvc._
import play.core.parsers.FormUrlEncodedParser
import services.SlackSignatureVerifierService
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
class SlackRequest[A](
val validateSignature: String => Try[String],
request: Request[A]
) extends WrappedRequest[A](request)
object SlackSignatureVerifyAction {
implicit class SlackRequestByteStringValidator(
slackRequest: SlackRequest[ByteString]
) {
def validateSignatureAgainstBody(): Try[Map[String, Seq[String]]] = {
val raw = slackRequest.body.utf8String
slackRequest.validateSignature(raw) map { _ =>
FormUrlEncodedParser.parse(new String(slackRequest.body.toArray))
}
}
}
val HEADERS_TIMESTAMP: String = "X-Slack-Request-Timestamp"
val HEADERS_SIGNATURE: String = "X-Slack-Signature"
}
class SlackSignatureVerifyAction #Inject() (
val parser: BodyParsers.Default,
slackSignatureVerifierService: SlackSignatureVerifierService
)(implicit ec: ExecutionContext)
extends ActionBuilder[SlackRequest, AnyContent]
with ActionRefiner[Request, SlackRequest]
with Logging {
override protected def executionContext: ExecutionContext = ec
override protected def refine[A](
request: Request[A]
): Future[Either[Result, SlackRequest[A]]] = {
val timestamp =
request.headers.get(SlackSignatureVerifyAction.HEADERS_TIMESTAMP)
val signature =
request.headers.get(SlackSignatureVerifyAction.HEADERS_SIGNATURE)
(timestamp, signature) match {
case (Some(timestamp), Some(signature)) =>
Future.successful {
val validate = (body: String) =>
slackSignatureVerifierService.validate(timestamp, body, signature)
Right(new SlackRequest[A](validate, request))
}
case _ =>
Future { Left(Unauthorized("Invalid signature headers")) }
}
}
}
You are right, there isn't an easy way to verify Hmac signatures on Play! projects. In the end, your approach seems to have a very reasonable implementation and could be easier adapted to other providers, such as GitHub and Stripe, that uses Hmac signatures.
I really think it could be a good open-source project to provide an Action with a wrapped Request or even a Service with a method to do custom signature validation for other providers. Why don't you help the Play community with an open-source project over GitHub?
I have created a new Play module to validate Hmac signatures. Details can be found here:
https://github.com/phelps-sg/play-hmac-signatures
I have a scenario where I want to verify that every request has a valid header attribute. This attribute is a digital signature which essentially is a hash of the request url and the request body, in this case a form-url-encoded body.
So I did the typical thing and created an action filter as follows:
class SomeActionFilter #Inject()(val executionContext: ExecutionContext) extends ActionFilter[Request] {
override protected def filter[A](request: Request[A]): Future[Option[Result]] = Future.successful {
// here I want to get the request body which I know is a form url encoded request body
val body = request.body //problem here is that body is of type A
}
}
What I really want is to get request.body to be of type Option[Map[String, Seq[String]]] which is what a form url encoded body is. I could do this, ie swap the type parameter A with AnyContent like this and then I can get at a request.body.asFormUrlEncoded
class SomeActionFilter #Inject()(val executionContext: ExecutionContext) extends ActionFilter[Request] {
override protected def filter[AnyContent](request: Request[AnyContent]): Future[Option[Result]] = Future.successful {
encoded request body
val body = request.body.asFormUrlEncoded
}
}
Is that the right thing to do here, to swap the type parameter like this? Or do I check if the body is of type Option[Map[String, Seq[String]]] using asInstanceOf and reject the request if it isn't? I'm a bit lost on how to approach this.
I'm looking for any possible solutions/plugins to capture request and response that come through Play 2.5.x for making a custom logging.
I've found https://www.playframework.com/documentation/2.5.x/ScalaHttpFilters for Filters, however it cannot get the request body. Is there any way to do that?
The expected result should be all informations in request and response include header and body, so I can construct a custom structure and log it in ElasticSearch.
You can imagine that Playframework works with a request as a pipeline.
A filter is a right place for logging requests, but, as you found, it has no access to the body. The main reason for this is because on this stage body is not parsed. The filter can reject an inappropriate request just by headers, no need to waste resources to parse the body. That is the main idea of the filters.
The next stage in a pipeline is an action. If you need to do something with the body of the request, then you need to use an actions composition. Unfortunately, you will need to wrap\replace every action with your composite action.
Documentation
https://www.playframework.com/documentation/2.5.x/ScalaActionsComposition
Example (from the documentation)
Define logging action:
def logging[A](action: Action[A])= Action.async(action.parser) { request =>
Logger.info("Calling action")
action(request)
}
object LoggingAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
block(request)
}
override def composeAction[A](action: Action[A]) = new Logging(action)
}
Use it:
def index = LoggingAction {
Ok("Hello World")
}
def index = Logging {
Action {
Ok("Hello World")
}
}
I'm developing a web service using Scala and Play Framework 2.5. My application has typical layered architecture. I have the following classes: model.User, repository.UserRepository, service.UserService and controllers.UserController and I'm trying to a good way to handle buisness logic errors without using Exception.
Consider the following case: a request for registration a new user was received. There are two parameters in the request body: email and password. These parameters are fed to UserService#registerUser which checks if the email is valid and if user with such email already exists. My solution is to make UserService#registerUser return as a result an Either[BuisnessFailure, Int] object. BuisnessFailure is a trait and is inherited by WrongEmailFormatFailure and UserAlreadyExistFailure.
If the email is not valid, UserService#registerUser returns Left(WrongEmailFormatFailure). If a user with such email already exists, UserService#registerUser returns Left(UserAlreadyExistFailure). And in case of success, UserService#registerUser returns Right(userRepository.create(User(email, password)).Afterwards, in controllers.UserController I can handle this case using pattern matching and send an appropriate response.
So, is this approach good enough to handle similar cases? Please, find my code below:
User:
package model
case class User(email: String, password: String)
UserRepository:
package repository
import model.User
class UserRepository {
def create(user: User): Int = ???
def find(email: String): Option[User] = ???
}
UserService:
package service
import model.User
import repository.UserRepository
import util.{BuisnessFailure, UserAlreadyExistFailure, WrongEmailFormatFailure}
class UserService {
private val userRepository: UserRepository = ???
def registerUser(email: String, password: String): Either[BuisnessFailure, Int] = {
if (userAlreadyExists(email))
Left(UserAlreadyExistFailure)
else
if (!isEmailValid(email))
Left(WrongEmailFormatFailure)
else
Right(userRepository.create(User(email, password)))
}
private def isEmailValid(email: String): Boolean = ???
private def userAlreadyExists(email: String): Boolean = ???
}
UserController:
package controller
import service.UserService
import util.{UserAlreadyExistFailure, WrongEmailFormatFailure}
class UserController extends play.api.Controller {
private val userService = new UserService
def signUp() = Action(parse.json) { implicit request =>
//obtaining email and password parameters from request body
val email = ???
val password = ???
userService.registerUser(email, password) match {
case Left(WrongEmailFormatFailure) => // send 400 code and appropriate error message
case Left(UserAlreadyExistFailure) => // send 400 code and appropriate error message
case Right(_) => // send response with 200 code
}
}
}
BuisnessFailure:
package util
sealed trait BuisnessFailure
case object UserAlreadyExistFailure extends BuisnessFailure
case object WrongEmailFormatFailure extends BuisnessFailure
This is exactly what we've done regarding error handling for one of our biggest projects and we didn't have any problem. Either should be used exactly like that. Left for errors and Right for results.
It's typesafe
No need to worry about concurrency
The code is scalable and maintainable
The only point is that, as most of Scala applications are non-block (async) people would use Future[Either[Error, Int]] rather than Either[Error, Int]. But still, it's OK, as whenever you decided to go for non-blocking you can easily wrap Either inside a Future and as I told you, no worries about concurrency issues.
I am building a security trait using AuthenticatedBuilder:
trait Security { self: Controller =>
object AuthenticatedAction extends AuthenticatedBuilder
(request => getUserFromRequest(request))
}
I have an implementation in that trait for this function:
def getUserFromRequest(request: RequestHeader) = {
// ...whatever...
}
This function looks for an authentication token in the request, validates it and maps it to a user id, which is then used to find a user in the database. If all is well, a User instance is returned, otherwise None. It's part of a wider authentication scheme, based pretty much on the play-angular-seed github project. Duplicating all of that code here is redundant, suffice to say this method either returns a User or None to signify authentication.
This lets me write a controller like this:
object Accounts extends Controller with Security {
def list = AuthenticatedAction { implicit request =>
// I can use request.user here to implement authorisation as needed
Ok(toJson(Account.list))
}
}
In this Controller, because of AuthenticatedAction I am guaranteed to have a User instance available via the request.user value. If there is no authorized user, the code block itself does not get executed and returns a Forbidden result.
This is all fine and works really well.
I have one use-case where I need to do all this authentication, but do some custom handling of the unauthorised case - i.e. I want to execute my own code block in this Controller when authorisation fails.
I know that AuthenticatedBuilder can accept such a function during construction:
def apply[U](userinfo: (RequestHeader) ⇒ Option[U],
onUnauthorized: (RequestHeader) ⇒ Result = ...): AuthenticatedBuilder[U]
I'm new to Scala, and I can't figure out the syntax/changes required to pass such a function from my Controller via my AuthenticatedAction object to the AuthenticatedBuilder. Ideally I would pass an optional onUnauthorized parameter, or I could have a separate AuthenticatedAction implementation I suppose.
How can it be done?
Objects cannot have parameter lists, so you need to use a method and create the action on the fly.
def Authenticated(unauth: RequestHeader => Result) =
new AuthenticatedBuilder(getUserFromRequest, unauth)