Skip to content

Instantly share code, notes, and snippets.

@chrisgibson
Created January 30, 2014 20:08
Show Gist options
  • Save chrisgibson/8717706 to your computer and use it in GitHub Desktop.
Save chrisgibson/8717706 to your computer and use it in GitHub Desktop.
Spray Token Authentication
import scala.concurrent.{ExecutionContext, Future}
import spray.routing.{AuthenticationFailedRejection, RequestContext}
import spray.routing.authentication.{Authentication, ContextAuthenticator}
/** Token based authentication for Spray Routing.
*
* Extracts an API key from the header or querystring and authenticates requests.
*
* TokenAuthenticator[T] takes arguments for the named header/query string containing the API key and
* an authenticator that returns an Option[T]. If None is returned from the authenticator, the request
* is rejected.
*
* Usage:
*
* val authenticator = TokenAuthenticator[User](
* headerName = "My-Api-Key",
* queryStringParameterName = "api_key"
* ) { key =>
* User.findByAPIKey(key)
* }
*
* def auth: Directive1[User] = authenticate(authenticator)
*
* val home = path("home") {
* auth { user =>
* get {
* complete("OK")
* }
* }
* }
*/
object TokenAuthenticator {
object TokenExtraction {
type TokenExtractor = RequestContext => Option[String]
def fromHeader(headerName: String): TokenExtractor = { context: RequestContext =>
context.request.headers.find(_.name == headerName).map(_.value)
}
def fromQueryString(parameterName: String): TokenExtractor = { context: RequestContext =>
context.request.uri.query.get(parameterName)
}
}
class TokenAuthenticator[T](extractor: TokenExtraction.TokenExtractor, authenticator: (String => Future[Option[T]]))
(implicit executionContext: ExecutionContext) extends ContextAuthenticator[T] {
import AuthenticationFailedRejection._
def apply(context: RequestContext): Future[Authentication[T]] =
extractor(context) match {
case None =>
Future(
Left(AuthenticationFailedRejection(CredentialsMissing, List()))
)
case Some(token) =>
authenticator(token) map {
case Some(t) =>
Right(t)
case None =>
Left(AuthenticationFailedRejection(CredentialsRejected, List()))
}
}
}
def apply[T](headerName: String, queryStringParameterName: String)(authenticator: (String => Future[Option[T]]))
(implicit executionContext: ExecutionContext) = {
def extractor(context: RequestContext) =
TokenExtraction.fromHeader(headerName)(context) orElse
TokenExtraction.fromQueryString(queryStringParameterName)(context)
new TokenAuthenticator(extractor, authenticator)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment