-
Notifications
You must be signed in to change notification settings - Fork 1
Controllers (Use)
Controllers: Basics | Use | Config | Service Requests | Users & Tokens
Some other useful bits of information:
Note: Implicit parameters and controller dependencies have been removed for brevity.
# Routes
GET / controllers.System.root
GET /test controllers.System.test
GET /login controllers.System.login(code: Option[String])
GET /logout controllers.System.logout
import javax.inject.Inject
import core3.config.StaticConfig
import core3.http.controllers.auth0.ClientController
import play.api.cache.SyncCacheApi
import play.api.libs.ws.WSClient
import scala.concurrent.{ExecutionContext, Future}
class System @Inject()(ws: WSClient, cache: SyncCacheApi /*...*/)
extends ClientController(
ws,
cache,
StaticConfig.get.getConfig(
"security.authentication.clients.<SOME_AUTH0_CLIENT_NAME>"
)
) {
def root() = PublicAction(
{ (request, user) => implicit val r = request
user match {
case Some(_) => Future.successful(Redirect("/test"))
case None => Future.successful(Redirect("/login"))
}
}
)
def test() = AuthorizedAction(
"test:view",
okHandler = { (request, user) => implicit val r = request
//... do some work ...
}
)
def login(codeOpt: Option[String] = None) = LoginAction(
{ implicit request => //success
Future.successful(Redirect("/"))
}, { implicit request => //not allowed
Future.successful(Unauthorized("Authentication failed"))
}, { implicit request => //should log in
Future.successful(Ok(views.html.system.login("Test - Login")))
}
)
def logout() = LogoutAction(Some("https://<some host & port>/"))
}Three Auth0 "components" are involved (in a way) in performing user authentication: Clients, Connections and Users (all are configured through Auth0's management page). Before a user can log in, the following conditions need to be met:
-
Connections- an appropriate credentials connection needs to be available and configured -
Clients- the client needs to exist, be configured and it needs to use the above connection (can be set inClients-><SOME_AUTH0_CLIENT_NAME>->Connections) -
Users-> the user needs to exist and it has to be associated with the above connection
The Auth0 Authorization extension needs to be installed and available in Extensions. User authorization is based on the permissions list under Permissions in the extension's management page. For more info on how to configure roles and groups in order to assign permissions to users, check the extension's docs.
Note: Tested with
Auth0 Authorizationversion 2.1
# Routes
GET / controllers.System.root
GET /public controllers.System.public
GET /internal controllers.System.internal
GET /system/login controllers.System.loginPage
POST /system/login controllers.System.login
GET /system/logout controllers.System.logout
import javax.inject.Inject
import core3.config.StaticConfig
import core3.database.dals.DatabaseAbstractionLayer
import core3.http.controllers.local.ClientController
import core3.http.responses.GenericResult
import play.api.cache.SyncCacheApi
import scala.concurrent.Future
class System @Inject()(cache: SyncCacheApi, db: DatabaseAbstractionLayer /*...*/)
extends ClientController(
cache,
StaticConfig.get.getConfig(
"security.authentication.clients.<SOME_LOCAL_AUTH_CLIENT_NAME>"
),
db
) {
def root() = PublicAction(
{ (request, user) => implicit val r = request
user match {
case Some(_) => Future.successful(Redirect("/internal"))
case None => Future.successful(Redirect("/public"))
}
}
)
def public() = PublicAction(
{ (request, user) => implicit val r = request
//... do some work ...
}
)
def internal() = AuthorizedAction(
"test:view",
okHandler = { (request, user) => implicit val r = request
//... do some work ...
}
)
def loginPage = PublicAction(
{ (request, user) => implicit val r = request
user match {
case Some(_) => Future.successful(Redirect("/"))
case None => Future.successful(Ok("Some login page"))
}
}
)
//Login action handler
def login() = LoginAction(
{ implicit request => //success
Future.successful(Ok(GenericResult(wasSuccessful = true).asJson))
}, { implicit request => //not allowed
Future.successful(
Unauthorized(
GenericResult(
wasSuccessful = false,
message = Some(s"Invalid user and/or password")
).asJson
)
)
}, { implicit request => //should log in
Future.successful(
Unauthorized(
GenericResult(
wasSuccessful = false,
message = Some(s"Login required")
).asJson
)
)
}
)
//Logout action handler
def logout() = LogoutAction() //use default
}During development it can be useful to define a test that initializes your credentials store (DB) with some test users. You can see an example here. At least one LocalUser (with type Client) will be needed.
When using Auth0 as the auth provider, having multiple routes is only possible by having multiple controllers, as the API configuration is bound to the route (API Identifier). Below is a simple example implementation with two routes/services.
Note: The naming of the routes, APIs, clients, controllers and actions is entirely up to you; this is just an example.
# Routes
POST /service/users controllers.UsersService.core
POST /service/clients controllers.ClientsService.core
Auth0 management - APIs
Two APIs are needed:
-
<SOME_AUTH0_API_NAME_FOR_CLIENT_SERVICE>withAPI Audience==https://<some host & port>/service/clients -
<SOME_AUTH0_API_NAME_FOR_USER_SERVICE>withAPI Audience==https://<some host & port>/service/users
Auth0 management - Clients
Two non-interactive clients are needed (if they do not exist):
<SOME_AUTH0_API_NAME_FOR_CLIENT_SERVICE><SOME_AUTH0_API_NAME_FOR_USER_SERVICE>
import javax.inject.{Inject, Singleton}
import core3.config.StaticConfig
import core3.http.controllers.auth0.ServiceController
import play.api.mvc._
import play.api.cache.SyncCacheApi
import play.api.libs.ws.WSClient
@Singleton
class ClientsService @Inject()(ws: WSClient, cache: SyncCacheApi /*...*/)
extends ServiceController(
ws,
cache,
StaticConfig.get.getConfig(
"security.authentication.services.<SOME_AUTH0_API_NAME_FOR_CLIENT_SERVICE>"
),
StaticConfig.get.getConfig(
"security.authentication.clients.<SOME_AUTH0_API_NAME_FOR_CLIENT_SERVICE>"
)
) {
def core() = ClientAwareAction(
"exec:any-workflow", //see `Scopes` below
(request: Request[AnyContent], clientID: String) => {
//... do some work ...
}
)
}import javax.inject.{Inject, Singleton}
import core3.config.StaticConfig
import core3.http.controllers.auth0.ServiceController
import core3.security.UserTokenBase
import play.api.mvc._
import play.api.cache.SyncCacheApi
import play.api.libs.ws.WSClient
@Singleton
class UsersService @Inject()(ws: WSClient, cache: SyncCacheApi /*...*/)
extends ServiceController(
ws,
cache,
StaticConfig.get.getConfig(
"security.authentication.services.<SOME_AUTH0_API_NAME_FOR_USER_SERVICE>"
),
StaticConfig.get.getConfig(
"security.authentication.clients.<SOME_AUTH0_API_NAME_FOR_USER_SERVICE>"
)
) {
def core() = UserAwareAction(
"exec:workflow", //see `Scopes` below
(request: Request[AnyContent], user: UserTokenBase) => {
//... do some work ...
}
)
}Auth0 scopes must be created for an API via Auth0 management -> APIs -> <SOME_AUTH0_API_NAME> -> Scopes. After that is done, a client that needs to interact with the service needs to be allowed to use those scopes and the API: Auth0 management -> APIs -> <SOME_AUTH0_API_NAME> -> Non Interactive Clients -> select Authorized for the appropriate client -> expand subsection -> Scopes -> select the appropriate scopes
# Routes
POST /service/users controllers.Service.users
POST /service/clients controllers.Service.clients
During development it can be useful to define a test that initializes your credentials store (DB) with some test users. You can see an example here. At least one LocalUser (with type Service) will be needed for client-aware actions and at least two LocalUsers (one with type Service and one with type Client) will be needed for user-aware actions.
import javax.inject.{Inject, Singleton}
import core3.config.StaticConfig
import core3.database.dals.DatabaseAbstractionLayer
import core3.http.controllers.local.ServiceController
import core3.security.UserTokenBase
import play.api.cache.SyncCacheApi
import play.api.mvc._
import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.duration._
@Singleton
class Service @Inject()(db: DatabaseAbstractionLayer, cache: SyncCacheApi /*...*/)
extends ServiceController(
cache,
StaticConfig.get.getConfig(
"security.authentication.clients.<SOME_LOCAL_AUTH_SERVICE_NAME>"
),
db
) {
def users() = UserAwareAction(
"exec:asUser",
(request: Request[AnyContent], user: UserTokenBase) => {
//... do some work ...
}
)
def clients() = ClientAwareAction(
"exec:asClient",
(request: Request[AnyContent], clientID: String) => {
//... do some work ...
}
)
}The local auth scopes map directly to a LocalUser's permissions field. So if a service user needs to access an action that requires exec:asUser, it will need to be in that user's permissions list.
Home | Getting Started | Structure | Containers | Workflows | Controllers