These docs are for v2.0. Click to read the latest docs for v7.0.

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.