Lightbend Activator

The Play Silhouette Seed Project

Activator will be EOL-ed on May 24, 2017.

We’re making it easier and simpler for developers to get started with Lightbend technologies. This unfortunately means that future releases of Play, Akka and Scala will no longer include Activator support, and Lightbend’s Activator server will be decommissioned by the end of 2017. Instead of supporting Activator to create and set up development projects, we'll be supporting standard Giter8 templates for sbt users and Maven archetypes for Maven users. So going forward,

To create new Lightbend projects

Instead of using the Activator command, make sure you have sbt 0.13.13 (or higher), and use the “sbt new” command, providing the name of the template. For example, “$ sbt new akka/hello-akka.g8”. You can find a list of templates here.

Also, as a convenience, the Lightbend Project Starter allows you to quickly create a variety of example projects that you just unzip and run.

To create new templates

If you want to create new templates, you can now do that in Giter8.

To migrate templates from Activator to Giter8

If you created Activator templates in the past, please consider migrating them to Giter8 with this simple process.

The Play Silhouette Seed Project

Christian Kaps
Source
March 19, 2017
seed auth oauth1 oauth2 openid credentials silhouette authentication scala play

A template for an application which uses Silhouette(http://silhouette.mohiva.com/) as authentication library.

How to get "The Play Silhouette Seed Project" on your computer

There are several ways to get this template.

Option 1: Choose play-silhouette-seed in the Lightbend Activator UI.

Already have Lightbend Activator (get it here)? Launch the UI then search for play-silhouette-seed in the list of templates.

Option 2: Download the play-silhouette-seed project as a zip archive

If you haven't installed Activator, you can get the code by downloading the template bundle for play-silhouette-seed.

  1. Download the Template Bundle for "The Play Silhouette Seed Project"
  2. Extract the downloaded zip file to your system
  3. The bundle includes a small bootstrap script that can start Activator. To start Lightbend Activator's UI:

    In your File Explorer, navigate into the directory that the template was extracted to, right-click on the file named "activator.bat", then select "Open", and if prompted with a warning, click to continue:

    Or from a command line:

     C:\Users\typesafe\play-silhouette-seed> activator ui 
    This will start Lightbend Activator and open this template in your browser.

Option 3: Create a play-silhouette-seed project from the command line

If you have Lightbend Activator, use its command line mode to create a new project from this template. Type activator new PROJECTNAME play-silhouette-seed on the command line.

Option 4: View the template source

The creator of this template maintains it at https://github.com/mohiva/play-silhouette-seed#master.

Option 5: Preview the tutorial below

We've included the text of this template's tutorial below, but it may work better if you view it inside Activator on your computer. Activator tutorials are often designed to be interactive.

Preview the tutorial

Introduction

What is the purpose of this tutorial ?

This tutorial is not really meant as a manual to use this seed, but more as a first step to begin to understand how Silhouette works. You should refer to the documentation for detailed explanations and you can ask questions on the Silhouette Forum.

Overview
  • Run Your Application : quick setup to see the result.
  • Endpoints : the tip of the iceberg.
  • Modules : how things are wired up.
  • Handle errors in endpoints : customize the behaviour in case of not authenticated or not authorized users.
  • Identity/IdentityService : the structure of a user object and how to get one.
  • DAOs : store the data.
  • Authorization : who can access what.
  • Conclusion

Run Your Application

You can already run your app through the activator ui or with the activator run command and visit http://localhost:9000. But you will not be able to sign up because the application tries to send an email through SendGrid, which has to be configured. And the same goes for the other authentication providers (Google, Facebook, ...) : you have to register your application and set the provider key and secret in silhouette.conf for each of them to work.
Therefore, if you just want to experiment with this project, you can make two small changes allowing you to signup. First you have to set the Play mailer into mock mode by adding the following line to your application.conf file :

play.mailer.mock = true
This tells the Play mailer to log the email instead of trying to send it. But now you will not be able to activate your account after signing up, so you have to initialize new accounts as already activated by slightly modifiying the SignUpController :
val user = User(
  userID = UUID.randomUUID(),
  loginInfo = loginInfo,
  firstName = Some(data.firstName),
  lastName = Some(data.lastName),
  fullName = Some(data.firstName + " " + data.lastName),
  email = Some(data.email),
  avatarURL = None,
  //activated = false
  activated = true // TODO delete to avoid activating all users by default
)
You should now be able to sign up and then sign in.

Endpoints

As explained in the documentation, the endpoints are the Actions and the WebSockets that are managed by Silhouette. This means, for example, that to make sure that only registered users can access to an endpoint of your application, you simply have to use a silhouette.SecuredAction instead of a standard Action, like for index in the ApplicationController.

These endpoints are provided by the silhouette: Silhouette[DefaultEnv] object which is injected in each controller using dependency injection. To see where it comes from you have to examine the modules (next section).

Modules

Silhouette uses dependency injection to separate the API definition from its implementation. The modules to customize the bindings from the traits to their implementation can be found in the modules package. We are in particular interested by the SilhouetteModule.
This file contains all the bindings related to Silhouette and is therefore rather long. We are going to look at the part concerning the Silhouette[DefaultEnv] object, which provides the endpoints. After that, the structure of the file should be clear enough for you to find what you need.

We see in the first line of the configure method that the Silhouette[DefaultEnv] we came across in the controller is bound to the class SilhouetteProvider[DefaultEnv]. To discover from where this one comes from you have to look into the Silhouette library itself. At the bottom of the Silhouette.scala file, you can observe that SilhouetteProvider[E <: Env] depends on Environment[E], SecuredAction, UnsecuredAction and UserAwareAction. The three Actions already have a default implementation bound (in SecuredAction for example), so the only one left to be bound is the Environment. We can now go back to the SilhouetteModule to find the provideEnvironment method.
The @Provides annotation means that every time an intance of Environment[DefaultEnv] has to be injected, this method will provide it. The dependencies of provideEnvironment are themselves bound in the configure method. The Environment provided here has the type parameter DefaultEnv, an arbitrary name chosen for the environment type of this application, defined in Env.scala. This trait defines which Identity (i.e. structure of a user, doc) and which Authenticator (i.e. mean of authentication, doc) are used in this project. See the documentation about the environment for more information.

In summary, a module has a configure method where all the bindings are declared and other methods annotated with @Provides to provide instances with specific arguments.

Handle errors in endpoints

If a user tries to access a secured enpoint without being authenticated, the default behaviour of Silhouette is to send a simple "not authenticated" message. The same goes if an authenticated user requests a page he is not authorized to access. To customize this behaviour, this seed defines two custom error handlers, CustomSecuredErrorHandler and CustomUnsecuredErrorHandler.
To enable them, the defauld handlers have first to be disabled. This is done in the application.conf file with the following lines :

play.modules.disabled += "com.mohiva.play.silhouette.api.actions.SecuredErrorHandlerModule"
 play.modules.disabled += "com.mohiva.play.silhouette.api.actions.UnsecuredErrorHandlerModule"
The new handlers can then be bound to the error handler traits in the SilhouetteModule :
bind[UnsecuredErrorHandler].to[CustomUnsecuredErrorHandler]
 bind[SecuredErrorHandler].to[CustomSecuredErrorHandler]

Identity/IdentityService

Identity is a trait in Silhouette representing a user (documentation). Its implementation in this seed is the User class. This class has to be specified in the environment type, here DefaultEnv (see the documentation about the environment).

Silhouette also needs an IdentityService, extended here by UserService and then implemented by UserServiceImpl. You can find the binding concerning these in the SilhouetteModule. The IdentityService is needed in the authentication process to get a user given his identity provider and his identifier, bundled in a LoginInfo object. Here a UserService also allows to save a user, making it an additional layer of abstraction above the user data access object (UserDAO) to simplify the saving process.

The identity of a user is accessible in every secured or user aware endpoint via its Request object. In the case of a SecuredAction you directly get the Identity from request.identity, and for an UserAwareAction it is an Option[Identity] since this type of endpoint also accepts unauthentified users.

DAOs

As you can see in the daos package, this seed contains data access objects (DAOs) for Users and for AuthTokens (tokens to activate a user via email or change the password). They are implemented to store the data in an in-memory HashMap for the example. To keep the data over a restart of your application you have to give these DAOs an other implementation storing the values in a database. You can then replace the implementation bound in the SilhouetteModule (or BaseModule for the AuthToken).

There are other DAOs in this application, but their implementation comes from Silhouette. You can find the bindings in the SilhouetteModule :

// Replace this with the bindings to your concrete DAOs
bind[DelegableAuthInfoDAO[PasswordInfo]].toInstance(new InMemoryAuthInfoDAO[PasswordInfo])
bind[DelegableAuthInfoDAO[OAuth1Info]].toInstance(new InMemoryAuthInfoDAO[OAuth1Info])
bind[DelegableAuthInfoDAO[OAuth2Info]].toInstance(new InMemoryAuthInfoDAO[OAuth2Info])
bind[DelegableAuthInfoDAO[OpenIDInfo]].toInstance(new InMemoryAuthInfoDAO[OpenIDInfo])

These are all AuthInfoDAOs which store the information concerning authentication, like the password for users signing in with credentials. You may wonder why not directly store the password in the User object, but remember that we want users to be able to sign in via other authentication providers like Google or Facebook. Our application will not have to store a password for these users, but other authentication information depending on the protocol (OAuth1, OAuth2 or OpenID). Therefore it makes more sense to split the auth info from the rest of the user and store it in a different DAO.
Like for the UserDAO, you will have to provide other implementations to persist the data.

Authorization

To specify which endpoints a user can access in your application, you can implement an Authorization (documentation). WithProvider is an example allowing only users which are authenticated via a specified provider. You can implement your own logic and combine authorizations with logical operators like described in the documentation.

Conclusion

If you are planning to use Silhouette in your project, you may think that this seed contains quite a lot of features and maybe you want to take only what is necessary. So here is a list of the classes and files you absolutely need to make Silhouette work :

  • A module binding the necessary implementations, or providing the instances when you need to specify the constructor arguments
  • An implementation of Identity
  • An implementation of IdentityService
  • A trait extending Env, like DefaultEnv

But if you are planning to use your application in production, this project contains a few things that come in handy (AuthTokenCleaner, security headers, ...) and it may therefore be easier to use it as seed from the beginning.