Last active
August 29, 2015 14:22
-
-
Save celaus/5e36511292fcf8074577 to your computer and use it in GitHub Desktop.
JWT-authentication in a proper Authentication HTTP header for Spray (http://spray.io/).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package service.common.config | |
/** | |
* Required configuration properties for authentication. | |
*/ | |
trait AuthenticationConfiguration { | |
/** | |
* The secret for signing tokens. | |
* @return The secret. | |
*/ | |
def secret: String | |
/** | |
* Duration until a token expires. | |
* @return A duration. | |
*/ | |
def tokenExpiration: org.joda.time.Duration | |
/** | |
* The name of the algorithm used to sign tokens. | |
* @return The string name. | |
*/ | |
def algorithmName: String | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import authentikat.jwt._ // https://github.com/jasongoodwin/authentikat-jwt | |
import spray.http.{HttpCredentials, HttpRequest, OAuth2BearerToken} | |
import spray.routing.RequestContext | |
import spray.routing.authentication.HttpAuthenticator | |
import com.fasterxml.jackson.core.JsonParseException | |
import com.typesafe.scalalogging.LazyLogging | |
import scala.concurrent.{ExecutionContext, Future} | |
import service.common.config.AuthenticationConfiguration | |
package service.web.authentication | |
/** | |
* Type definition for easier handling. | |
*/ | |
package object authentication { | |
type TokenAuthenticator[T] = Option[ Map[String, String]] ⇒ Future[Option[T]] | |
} | |
/** | |
* Implements HTTP-Header-based authentication with a JSON Web Token to use in Spray. | |
* @param tokenAuthenticator The authenticator method. | |
* @param realm A resource or domain where access is requested. | |
* @param authenticationConfig The configuration for JWT (secret, algorithm). | |
* @param executionContext ... Spray stuff | |
* @tparam U ... Spray stuff | |
*/ | |
class HttpJWTAuthenticator[U](val tokenAuthenticator: TokenAuthenticator[U], val realm: String, val authenticationConfig: AuthenticationConfiguration)(implicit val executionContext: ExecutionContext) | |
extends HttpAuthenticator[U] with LazyLogging { | |
def getChallengeHeaders(httpRequest: HttpRequest) = List() | |
override def authenticate(credentials: Option[HttpCredentials], ctx: RequestContext): Future[Option[U]] = try { | |
tokenAuthenticator { | |
credentials.getOrElse(None) match { | |
case t: OAuth2BearerToken => if (JsonWebToken.validate(t.token, authenticationConfig.secret)) | |
t.token match { | |
case JsonWebToken(header, claims, signature) => Some(claims.asSimpleMap.get) | |
case _ => None | |
} | |
else None | |
case _ => None | |
} | |
} | |
} | |
catch { | |
case e: JsonParseException => Future(None) | |
} | |
} | |
/** | |
* Object to apply authentication. | |
*/ | |
object JWTAuthentication { | |
def apply[U](tokenAuthenticator: TokenAuthenticator[U], realm: String, authenticationConfig: AuthenticationConfiguration)(implicit ec: ExecutionContext): HttpAuthenticator[U] = | |
new HttpJWTAuthenticator[U](tokenAuthenticator, realm, authenticationConfig) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This method should verify the contents of the JWT claims set and make it a proper object. | |
def authenticator(claims: Option[Map[String, String]])(implicit ec: ExecutionContext): Future[Option[AuthenticationInfo]] = Future { ... } | |
// the route. notice how "get" and "authenticate" are nested: CORS traits then can rely on MethodRejection | |
lazy val route = pathPrefix("a" / "prefix") { | |
pathEndOrSingleSlash { | |
get { | |
authenticate(JWTAuthentication(authenticator, "realm", authenticationConfigurationInstance)) { authInfo => | |
// do stuff here | |
complete("hello world") | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment