Authorization
Silhouette provides a way to add authorization logic to your secured endpoints. This is done by implementing an Authorization
object that is passed to all SecuredRequestHandler
and SecuredAction
as a parameter.
After checking if a user is authenticated the Authorization
instance is used to verify whether the execution should be allowed or not.
/**
* A trait to define Authorization objects that let you hook
* an authorization implementation in secured endpoints.
*
* @tparam I The type of the identity.
* @tparam A The type of the authenticator.
*/
trait Authorization[I <: Identity, A <: Authenticator] {
/**
* Checks whether the user is authorized to execute an endpoint or not.
*
* @param identity The current identity instance.
* @param authenticator The current authenticator instance.
* @param request The current request.
* @tparam B The type of the request body.
* @return True if the user is authorized, false otherwise.
*/
def isAuthorized[B](identity: I, authenticator: A)(
implicit request: Request[B]): Future[Boolean]
}
Below is a sample implementation that only grants access to users that logged in using a given provider. This example comes in two variants. The first has a defined authenticator type which is only applicable to actions which are defined with the same authenticator type. The second example uses a generic authenticator type which is applicable to any kind of action, regardless of the authenticator type defined for this action. The second case can also be used with a generic identity and it's useful if the authorization depends either on the identity or the authenticator.
case class WithProvider(provider: String) extends Authorization[User, CookieAuthenticator] {
def isAuthorized[B](user: User, authenticator: CookieAuthenticator)(
implicit request: Request[B]) = {
Future.successful(user.loginInfo.providerID == provider)
}
}
case class WithProvider[A <: Authenticator](provider: String) extends Authorization[User, A] {
def isAuthorized[B](user: User, authenticator: A)(
implicit request: Request[B]) = {
Future.successful(user.loginInfo.providerID == provider)
}
}
Here’s how you would use it:
class Application(silhouette: Silhouette[DefaultEnv]) extends Controller {
def myAction = silhouette.SecuredAction(WithProvider("twitter")) { implicit request =>
// do something here
}
}
class Application(silhouette: Silhouette[DefaultEnv]) extends Controller {
def myAction = silhouette.SecuredAction(WithProvider[DefaultEnv#A]("twitter")) { implicit request =>
// do something here
}
}
Info
Errors for not authorized users can be caught with the global or local error handlers.
Logic Operator
You can use the logical !
, &&
and ||
operators to create logical expressions with your Authorization
instances. You also need to have an implicit ExecutionContext
in scope.
def myAction = silhouette.SecuredAction(!WithProvider("twitter")) { implicit request =>
// do something here
}
def myAction = silhouette.SecuredAction(WithProvider("twitter") && WithProvider("facebook")) {
implicit request => // do something here
}
def myAction = silhouette.SecuredAction(WithProvider("twitter") || WithProvider("facebook")) {
implicit request => // do something here
}
Updated less than a minute ago