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.
*/
trait Authorization[I <: Identity] {
/**
* Checks whether the user is authorized to execute an endpoint or not.
*
* @param identity The identity to check for.
* @param request The current request header.
* @param lang The current lang.
* @return True if the user is authorized, false otherwise.
*/
def isAuthorized(identity: I)(implicit request: RequestHeader, lang: Lang): Boolean
}
This is a sample implementation that only grants access to users that logged in using a given provider:
case class WithProvider(provider: String) extends Authorization[User] {
def isAuthorized(user: User)(implicit request: RequestHeader, lang: Lang) = {
user.loginInfo.providerID == provider
}
}
Here’s how you would use it:
def myAction = SecuredAction(WithProvider("twitter")) { implicit request =>
// do something here
}
For unauthorized users you can implement a global or local fallback handler similar to the fallback handlers for unauthenticated users.
Logic Operator
New in version 2.0
You can use the logical !
, &&
and ||
operators to create logical expressions with your Authorization
instances.
def myAction = SecuredAction(!WithProvider("twitter")) { implicit request =>
// do something here
}
def myAction = SecuredAction(WithProvider("twitter") && WithProvider("facebook")) {
implicit request => // do something here
}
def myAction = SecuredAction(WithProvider("twitter") || WithProvider("facebook")) {
implicit request => // do something here
}
Fallback handler
If the access to a secured endpoint will be denied then it's possible to provide a fallback handler to handle the incoming request and return an appropriate result.
Global Fallback
You can mix the SecuredSettings
trait into your Global
object. This trait provides a method called onNotAuthorized
. If you implement this method, then every time a user calls an endpoint on which he isn't authorized, the result specified in the global fallback method will be returned.
object Global extends GlobalSettings with SecuredSettings {
/**
* Called when a user is authenticated but not authorized.
*
* As defined by RFC 2616, the status code of the response should be 403 Forbidden.
*
* @param request The request header.
* @param lang The currently selected language.
* @return The result to send to the client.
*/
override def onNotAuthorized(request: RequestHeader, lang: Lang) = {
Some(Future.successful(Forbidden("Not authorized")))
}
}
Local Fallback
Every controller which is derived from Silhouette
base controller has a method called onNotAuthorized
. If you override these method, then you can return a not-authorized result similar to the global fallback but only for this specific controller. The local fallback has precedence over the global fallback.
class Application(env: Environment[User, CookieAuthenticator])
extends Silhouette[User, CookieAuthenticator] {
/**
* Implement this to return a result when the user is authenticated but not authorized.
*
* As defined by RFC 2616, the status code of the response should be 403 Forbidden.
*
* @param request The request header.
* @return The result to send to the client.
*/
override def onNotAuthorized(request: RequestHeader): Option[Future[Result]] = {
Some(Future.successful(Forbidden("Not authorized")))
}
/**
* Renders the index page.
*
* @returns The result to send to the client.
*/
def index = SecuredAction(WithProvider("twitter")) { implicit request =>
Ok(views.html.index(request.identity))
}
}
Note
If you don’t implement one of the both fallback methods, a 403 response with a simple message will be displayed to the user.
Updated less than a minute ago