Skip to content

Instantly share code, notes, and snippets.

@tdrozdowski
Last active December 27, 2015 19:49
Show Gist options
  • Save tdrozdowski/7379847 to your computer and use it in GitHub Desktop.
Save tdrozdowski/7379847 to your computer and use it in GitHub Desktop.
Adding Basic Auth to your Scala Play App. Requires Play 2.2.x
// step one - build your ActionBuilder
/**
* Created by terry on 10/1/13.
*/
object BasicAuthSecured extends ActionBuilder[AuthorizedRequest] with HeaderNames with Results {
protected def invokeBlock[A](request: Request[A], block: (AuthorizedRequest[A]) => Future[SimpleResult]): Future[SimpleResult] = {
BasicAuthService.authorize(request.headers.get(AUTHORIZATION)).map {
identity =>
block(AuthorizedRequest(identity, request))
} getOrElse (Future.successful(onUnauthorized))
}
/**
* Redirect to login if the user in not authorized.
*/
def onUnauthorized = Unauthorized.withHeaders(WWW_AUTHENTICATE -> "Basic realm=\"xymox.net\"")
}
// step 2 - build the BasicAuthService (I've placed it in the same file as the above in my impl - up to you how to do this)
object BasicAuthService {
def authorize(maybeHeader : Option[String]) = {
// TODO - you add your implemenation here, important thing is that it returns an Option[String] where the string is the user identifier
// below is how I handled it with my custom IdentityService
userCredentials(maybeHeader).flatMap {
credentials =>
IdentityService.findByEmailPassword(credentials.email, credentials.password).map {
identity =>
identity.email
}
}
}
/**
* Decode the basic auth header, if it exists.
* @param auth
* @return
*/
private def decodeBasicAuth(auth: String) : Option[UserCredentials] = {
auth.split(" ").drop(1).headOption.flatMap { encoded =>
new String(org.apache.commons.codec.binary.Base64.decodeBase64(encoded.getBytes)).split(":").toList match {
case u :: p :: Nil => Some(UserCredentials(u,p))
case _ => None
}
}
}
/**
* Function that handled looking up user credentials in the db. For now, we just return the username if we find a match.
*
* @param creds
* @return
*/
def userLookup(creds : UserCredentials) = {
// TODO - determine how to look up User Credentials
}
/**
*
* @param request
* @return
*/
private def userCredentials(header: Option[String]) : Option[UserCredentials] = {
header.map{ basicAuth =>
decodeBasicAuth(basicAuth)
}.getOrElse(None)
}
}
// Here are some case classes needed to tie this all together
case class UserCredentials(email : String, password : String)
case class AuthorizedRequest[A](email : String, request : Request[A]) extends WrappedRequest[A](request)
// Sample usage - in some Controller
def securePing = BasicAuthSecured {
request =>
Ok(Json.obj("status" -> "ok", "info" -> request.email))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment