Last active
December 27, 2015 19:49
-
-
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
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
// 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\"") | |
} |
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
// 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) | |
} | |
} |
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
// 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) |
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
// 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