Skip to content

Instantly share code, notes, and snippets.

@Otann
Created March 6, 2015 15:30
Show Gist options
  • Save Otann/9ebcca734ae08d730434 to your computer and use it in GitHub Desktop.
Save Otann/9ebcca734ae08d730434 to your computer and use it in GitHub Desktop.
package jp.t2v.lab.play2.auth
import scala.reflect.ClassTag
/** Enables authentication for separate entities */
class CustomCacheIdContainer[Id: ClassTag](suffix: String) extends CacheIdContainer[Id] {
override private[auth] val tokenSuffix = s":$suffix:token"
override private[auth] val userIdSuffix = s":$suffix:userId"
}
package jp.t2v.lab.play2.auth
import play.api.libs.Crypto
import play.api.mvc.{DiscardingCookie, RequestHeader, Controller, Result}
import scala.concurrent.{Future, ExecutionContext}
/**
* Rewrites authentication mechanism of storing/retrieval token in Request/Result
* @see [[jp.t2v.lab.play2.auth.CookieUpdater]]
* @see [[jp.t2v.lab.play2.auth.AuthenticityToken]]
*
* Mapping of concepts
* CookieName -> HeaderName
*/
/**
* Mixin for async authorization
* Your secured controllers should mix in this instead of AsyncAuth
*/
trait HeaderAsyncAuth extends AsyncAuth with HeaderSupport { self: AuthConfig with Controller =>
private[auth] override def restoreUser(implicit request: RequestHeader, context: ExecutionContext): Future[(Option[User], CookieUpdater)] = {
(for {
header <- request.headers.get(cookieName)
token <- verifyHeaderHmac(header)
} yield for {
Some(userId) <- idContainer.get(token)
Some(user) <- resolveUser(userId)
_ <- idContainer.prolongTimeout(token, sessionTimeoutInSeconds)
} yield {
Option(user) -> bakeCookie(token) _
}) getOrElse {
Future.successful(Option.empty -> identity)
}
}
}
/**
* Mixin for normal authorization
* Your Authorization controller should mix in this instead of LoginLogout
*/
trait HeaderLoginLogout extends LoginLogout with HeaderSupport { self: AuthConfig with Controller =>
override def gotoLogoutSucceeded(result: => Future[Result])(implicit request: RequestHeader, ctx: ExecutionContext): Future[Result] = {
request.headers.get(cookieName) flatMap verifyHeaderHmac foreach idContainer.remove
result
}
}
/**
* Override cookie baking mechanics and instead responds with header
* your AuthConfig should mix in this
*/
trait HeaderSupport extends CookieSupport { self: AuthConfig =>
override def bakeCookie(token: String)(result: Result): Result = {
val value = Crypto.sign(token) + token
result.withHeaders(cookieName -> value)
}
def verifyHeaderHmac(headerValue: String): Option[String] = {
val (signature, token) = headerValue.splitAt(40)
if (Crypto.sign(token) == signature) Some(token) else None
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment