Spray authenticate directive : A decoupled way to authenticate your API


Organization exposes critical business data or user’s private data through API. It is required to protect API against any unauthorized access, attack or security breach. In this blog I am not going to discuss API security mechanism. Here I want to detailed out the separation of API authentication from underlying RESTful Api.

Authentication / Authorization is cross cutting concern and it must be decoupled from business logic. In this post I am going to discuss Spray’s authenticate directive which handle authentication/authorization in decoupled way.

From Spray’s documentation.
“Directives” are small building blocks of which you can construct arbitrarily complex route structures.

A directive does one or more of the following:

  • Transform the incoming RequestContext before passing it on to its inner Route
  • Filter the RequestContext according to some logic, i.e. only pass on certain requests and reject all others
  • Extract values from the RequestContext and make them available to its inner Route as “extractions”
  • Complete the request

 authenticate directive is nothing but a special directive with takes a special function which would be used to authenticate inbound requests. At lowest level authenticate takes Future[Authentication[T]] or ContextAuthention[T] as argument.

Future[Authentication[T]] takes authentication data from lexical scope, while ContextAuthentication[T] takes authentication data from RequestContext. Actually ContextAuthentication is a higher order function which takes RequetContext as argument and return Future[Authentication[T]] .

Let’s take an example to see how this nice feature can be a useful tool to authenticate the api in decoupled way.

There are lot of available implementations build over authenticate directive like UserPass authenticator, Token Authenticator, Http Authenticator and so on. Recently I was working on a project where It is required to authenticate the api against access token, user pass or app_key / app_secret. I want to use a generic authenticator to do all kind of authentication so I decided to tweak existing implementation. In this implementation base trait is RestAuthenticator.scala. Code looks as follows

trait RestAuthenticator[U] extends ContextAuthenticator[U] {

type ParamExtractor = RequestContext => Map[String, String]

val keys: List[String]

val authenticator: Map[String, String] => Future[Option[U]]

val extractor: ParamExtractor = (ctx: RequestContext) => ctx.request.uri.query.toMap

def apply(ctx: RequestContext): Future[Authentication[U]] = {
val queryParameters = extractor(ctx)
authenticator(queryParameters) map {
case Some(entity) ⇒ Right(entity)
case None ⇒
val cause = if (queryParameters.isEmpty) CredentialsMissing else CredentialsRejected
Left(AuthenticationFailedRejection(cause, getChallengeHeaders(ctx.request)))
}
}

Different type of authenticator can be constructed by extending RestAuthenticator. In derived class we just need to supply two things which are abstract in RestAuthenticator.

a) keys : Parameters which need to extract from query parameters. It may be “access_token” In case AccessTokenAuthenticator OR “app_key” and “app_secret in case of AppAuthenticator.

b) authenticator : This function contains custom logic to authenticate credentials or token.

Following is an example of AccessTokenAuthenticator.

object AccessTokenHandler {

case class AccessTokenAuthenticator(val keys: List[String] = defaultKeys,
val authenticator: Map[String, String] => Future[Option[User]] = defaultAuthenticator) extends RestAuthenticator[User] {
def apply(): Directive1[User] = authenticate(this)
}

val defaultKeys = List("access_token")
val defaultAuthenticator = (params: Map[String, String]) => Future {
val mayBeUser = params.get(defaultKeys(0))
mayBeUser flatMap { token =>
/*
* get user form database , replace None with proper method once database service is ready.
* getUserFromAccessToken(token)
*/
None
}
}
}

Following is a UserPassAuthenticator.

object UserPassHandler {

case class UserPassAuthenticator(val keys: List[String] = defaultKeys,
val authenticator: Map[String, String] => Future[Option[User]] = defaultAuthenticator)
extends RestAuthenticator[User] {

def apply(): Directive1[User] = authenticate(this)
}

val defaultKeys = List("username", "password")
val defaultAuthenticator = authFunction _

def authFunction(params: Map[String, String]): Future[Option[User]] = Future {
val emailOpt = params.get(defaultKeys(0))
val passwordOpt = params.get(defaultKeys(1))

val mayBeUser = for {
email <- emailOpt
password <- passwordOpt
user <- {
//get user form database , replace None with proper method once database service is ready.
//getUserByCredential(email, password)
None
}
} yield user
mayBeUser
}
}

Both of the Authenticators are parameterized case classes so we can supply dummy authenticator function while testing.

Integrate the authenticators with routes

val tokenProtectedRoutes: Route = {
pathPrefix("user") {
AccessTokenAuthenticator() { user =>

/*
* Following are protected resources.
* Requires valid access_token of  user.
*/

get {
path("items") {
complete(HttpResponse(OK, "Display user's item"))
}
}

}
}
}

val userPassProtectedRoutes = {
pathPrefix("authenticate") {
UserPassAuthenticator() { user =>
appCredentialHandlerForTest { consumer =>
/*
* Following are protected resources.
* Request would gone through userPass and appCredential authenticator to unlock the resources.
*/

get {
pathSingleSlash {
complete(HttpResponse(OK, "Welcome User !!!"))
}
}
}
}
}
}

Following route shows how authenticators can be composed. Here userPassHandlerForTest and appCredentialHandlerForTest has been composed to validate app and user’s credentials.

val userPassProtectedRoutes = {
pathPrefix("authenticate") {
userPassHandlerForTest { user =>
appCredentialHandlerForTest { consumer =>
/*
* Following are protected resources.
* Request would gone through userPass and appCredential authenticator to unlock the resources.
*/

get {
pathSingleSlash {
complete(HttpResponse(OK, "Welcome User !!!"))
}
}
}
}
}
}

Code can be pulled from github.

Advertisements

About mayankbairagi

Software Developer
This entry was posted in Scala. Bookmark the permalink.

2 Responses to Spray authenticate directive : A decoupled way to authenticate your API

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s