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
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.
Following is a UserPassAuthenticator.
Both of the Authenticators are parameterized case classes so we can supply dummy authenticator function while testing.
Integrate the authenticators with routes
Following route shows how authenticators can be composed. Here userPassHandlerForTest and appCredentialHandlerForTest has been composed to validate app and user’s credentials.
Code can be pulled from github.