Silhouette Documentation Release 2.0-RC1

Christian Kaps

February 16, 2015

Contents

1 Basics 3 1.1 Features...... 3 1.2 Examples...... 4 1.3 Releases...... 4 1.4 Contribute...... 6 1.5 Help...... 8

2 How it works 9 2.1 Environment...... 9 2.2 Actions...... 9 2.3 Identity...... 15 2.4 Authenticator...... 17 2.5 Providers...... 21 2.6 Error handling...... 26 2.7 Caching...... 26 2.8 Events...... 27 2.9 Logging...... 28 2.10 Testing...... 29

3 Configuration 35 3.1 Configuration...... 35

i ii Silhouette Documentation, Release 2.0-RC1

Silhouette is an authentication library for applications that supports several authentication methods, including OAuth1, OAuth2, OpenID, Credentials, Basic Authentication or custom authentication schemes. It can be integrated as is, or used as a building block and customized to meet specific application requirements, thanks to its loosely coupled design. The project is named after the fictional crime fighter character Silhouette, from the Watchmen graphic novel and movie.

Contents 1 Silhouette Documentation, Release 2.0-RC1

2 Contents CHAPTER 1

Basics

1.1 Features

Easy to integrate Silhouette comes either with an Activator template for traditional web pages or an Activator template for single-page applications that gives you a complete sample application which is 100% customizable. Simply select the template play-silhouette-seed or play-silhouette-angular-seed in your Activator UI. It has never been easier to start a new Silhouette application. For other examples, please visit the Examples section in the Documentation. Authentication support Out of the box support for leading social services such as Twitter, Facebook, Google, LinkedIn and GitHub. Silhouette also includes a credentials and basic authentication provider that supports local login functionality. Client agnostic Silhouette comes with a set of stateless as well as stateful authenticator implementations which allows an application to handle a wide range of different clients like traditional web browsers as also native(desktop, mobile, ...) apps. Asynchronous, non-blocking operations We follow the Reactive Manifesto. This means that all requests and web service calls are asynchronous, non-blocking operations. For the event handling part of Silhouette we use Akka’s Event Bus implementation. Lastly, all persistence interfaces are defined to return Scala Futures. Very customizable, extendable and testable From the ground up Silhouette was designed to be as customizable, extendable and testable as possible. All compo- nents can be enhanced via inheritance or replaced based on their traits, thanks to its loosely coupled design. Internationalization support Silhouette makes it very easy to internationalize your application by making the Play Framework’s Request and Lang available where they are needed. Well tested Silhouette is a security component which protects your users from being compromised by attackers. Therefore we aim for complete code coverage with unit and integration tests. Follows the OWASP Authentication Cheat Sheet Silhouette implements and promotes best practices such as described by the OWASP Authentication Cheat Sheet like Password Strength Controls, SSL Client Authentication or use of authentication protocols that require no password.

3 Silhouette Documentation, Release 2.0-RC1

1.2 Examples

This page lists some examples which show how Silhouette can be used in various kinds of projects. Silhouette Seed Template The Silhouette Seed project is an Activator template which shows how Silhouette can be used in a Play Framework application. It’s a starting point which can be extended to fit your needs. Link

Silhouette Angular Seed Template The Silhouette Angular Seed project is an Activator template which shows how Silhouette can be used to create a SPA with AngularJS and Play. It’s a starting point which can be extended to fit your needs. Link

Silhouette Slick Seed Template A fork of the Silhouette Seed project which uses Slick as database access library. Link

Silhouette Cake Seed Template Seed project for Play Framework with Silhouette, using the Cake Pattern for dependency injection. Link

Silhouette Rest Seed Seed project for Play Framework with Silhouette, exposing a rest api for signup and signin. Link

Play 2.3 Multidomain Auth Project for Play Framework with Silhouette, using a not strict implementation of Thin Cake Pattern and showing how to add an authentication and authorizaton layer. The project is split in two main subprojects: a public web page that uses a complete authentication layer (including sign up with email confirmation and reset passwords), and an administration web page that uses also a simple authorization layer based on roles. Link

1.3 Releases

1.3.1 Release Versions

Silhouette 2.0

This is the first release candidate of the next stable 2.0 release and it’s available for Scala 2.10 / 2.11 and Play 2.3.

4 Chapter 1. Basics Silhouette Documentation, Release 2.0-RC1

Documentation

• HTML • HTML (Zip) • PDF • EPUB • API

Installation

To install this version add the following dependency to your build.sbt file. libraryDependencies ++= Seq( "com.mohiva"%% "play-silhouette"% "2.0-RC1" )

Silhouette 1.0

This is the previous stable release and it’s available for Scala 2.10 / 2.11 and Play 2.3.

Documentation

• HTML • HTML (Zip) • PDF • EPUB • API

Installation

To install this version add the following dependency to your build.sbt file. libraryDependencies ++= Seq( "com.mohiva"%% "play-silhouette"% "1.0" )

1.3.2 Old Versions

Silhouette 0.9

Available for Scala 2.10 and Play 2.2.

1.3. Releases 5 Silhouette Documentation, Release 2.0-RC1

Documentation

• HTML • HTML (Zip) • PDF • EPUB • API

Installation

To install this version add the following dependency to your build.sbt file. libraryDependencies ++= Seq( "com.mohiva"%% "play-silhouette"% "0.9" )

1.3.3 Notes

The master branch contains the current development version. It should be working and passing all tests at any time, but it’s unstable and represents a work in progress. Released versions are indicated by tags. Release numbers will follow Semantic Versioning.

1.4 Contribute

1.4.1 How to contribute

Silhouette is an open source project. Contributions are appreciated. See the current list of contributors and join them! Some ways in which you can contribute are: reporting errors, improving documentation, adding examples, adding support for more services, fixing bugs, suggesting new features, adding test cases, translating messages, and whatever else you can think of that may be helpful. If in doubt, just ask.

1.4.2 The project structure

Silhouette is dived into the api and impl packages. The definition of these both packages is as follow:

API Package

The collection of traits and utility classes that form the stable API of Silhouette.

Impl Package

The reference implementation of Silhouette.

6 Chapter 1. Basics Silhouette Documentation, Release 2.0-RC1

Test Package

Contains helper classes and objects to easily test an application based on Silhouette.

1.4.3 Development workflow

Development is coordinated via GitHub. Ideas for improvements are discussed using issues. For a more streamlined experience for all people involved, we encourage contributors to follow the practices described at GitHub workflow for submitting pull requests. Scala source code should follow the conventions documented in the Scala Style Guide. Additionally, acronyms should be capitalized. To have your code automatically reformatted, run this command before committing your changes: scripts/reformat

Important: After submitting your pull request, please watch the result of the automated Travis CI build and correct any reported errors or inconsistencies.

1.4.4 Help to improve the documentation

Every software project is only as good as its documentation. So we do our best to cover all the code with a good structured, meaningful and up-to-date documentation. But like in the most open source projects, time is a precious commodity. We appreciate all help to improve the documentation.

Edit on GitHub

For small typo changes the documentation can be edited directly on GitHub. This is very easy by following the “Edit on GitHub” button at the top of th page. After you have made your changes you can commit it with a meaningful commit message. It will then automatically create a new pull request with your proposed changes.

Edit it locally

The documentation is written in RST which can be rendered by Sphinx into many output formats like HTML, PDF and many more. To render the documentation into static HTML which looks the same as this documentation on “Read the docs”, you must install Sphinx and the “Read the Docs” theme. pip install sphinx sphinx_rtd_theme

Note: Python must be installed on your system. Please consult the documentation of your OS on how to do it.

If you have edited the documentation files, you can create the static HTML files by executing the following command in the docs directory. make

The documentation can then be found in the _build/html directory. Browse the index.html with your favorite browser to view the changes.

1.4. Contribute 7 Silhouette Documentation, Release 2.0-RC1

1.4.5 License and Copyright

By submitting work via pull requests, issues, wiki, or any other means, contributors indicate their agreement to publish their work under this project’s license and also attest that they are the authors of the work and grant a copyright license to the Mohiva Organisation, unless the contribution clearly states a different copyright notice (e.g., it contains original work by a third party). The code is licensed under Apache License v2.0 and the documentation under CC BY 3.0. This project is derived from SecureSocial, Copyright 2013 Jorge Aliss (jaliss at gmail dot com) - twitter: @jaliss. Thanks to Jorge Aliss for his great work.

1.5 Help

If you need help with integrating Silhouette into your project, don’t hesitate to ask questions using our mailing list or on Stack Overflow.

8 Chapter 1. Basics CHAPTER 2

How it works

2.1 Environment

The environment defines the key components needed by a Silhouette application. It consists of the identity implementa- tion, the authenticator implementation, the identity service implementation, the authenticator service implementation, the provider implementation and the event bus implementation. A configured environment is needed in every derived Silhouette controller implementation and should be provided through dependency injection. Silhouette plays well with both compile time and runtime dependency injection. You can find examples of both approaches on the example page.

2.2 Actions

2.2.1 Securing your Actions

Silhouette provides a replacement for Play’s built in Action class named SecuredAction. This action intercepts requests and checks if there is an authenticated user. If there is one, the execution continues and your code is invoked. class Application(env: Environment[User, CookieAuthenticator]) extends Silhouette[User, CookieAuthenticator]{

/** * Renders the index page. * * @returns The result to send to the client. */ def index = SecuredAction { implicit request => Ok(views.html.index(request.identity)) } }

There is also a UserAwareAction that can be used for actions that need to know if there is a current user but can be executed even if there isn’t one. class Application(env: Environment[User, CookieAuthenticator]) extends Silhouette[User, CookieAuthenticator]{

/** * Renders the index page. *

9 Silhouette Documentation, Release 2.0-RC1

* @returns The result to send to the client. */ def index = UserAwareAction { implicit request => val userName = request.identity match { case Some(identity) => identity.fullName case None => "Guest" } Ok("Hello %s".format(userName)) } }

For unauthenticated users you can implement a global or local fallback action.

Request Handlers

New in version 2.0. The base implementations for these actions are encapsulated into request handlers. For the SecuredAction there exists a SecuredRequestHandler and for the UserAwareAction there exists a UserAwareRequestHandler. You can use these handlers in default Play actions.

/** * An example for a secured request handler. */ def securedRequestHandler = Action.async{ implicit request => SecuredRequestHandler { securedRequest => Future.successful(HandlerResult(Ok, Some(securedRequest.identity))) }.map{ case HandlerResult(r, Some(user)) => Ok(Json.toJson(user.loginInfo)) case HandlerResult(r, None) => Unauthorized } }

/** * An example for an user aware request handler. */ def userAwareRequestHandler = Action.async{ implicit request => UserAwareRequestHandler { userAwareRequest => Future.successful(HandlerResult(Ok, userAwareRequest.identity)) }.map{ case HandlerResult(r, Some(user)) => Ok(Json.toJson(user.loginInfo)) case HandlerResult(r, None) => Unauthorized } }

Request handlers have the advantage that they can transport the Result to send to the client and additional data out of these handlers. This may be useful for securing web sockets.

Global Fallback

You can mix the SecuredSettings trait into your Global object. This trait provides a method called onNotAuthenticated. If you implement this method, then every time a user calls a restricted action, the re- sult specified in the global fallback method will be returned. object Global extends GlobalSettings with SecuredSettings {

10 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

/** * Called when a user isn’t authenticated. * * @param request The request header. * @param lang The current selected lang. * @return The result to send to the client. */ override def onNotAuthenticated(request: RequestHeader, lang: Lang) = { Some(Future.successful(Unauthorized("No access"))) } }

Local Fallback

Every controller which is derived from the Silhouette base controller has a method called notAuthenticated. If you override these method, then you can return a not-authenticated 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]{

/** * Called when a user isn’t authenticated. * * @param request The request header. * @return The result to send to the client. */ override def notAuthenticated(request: RequestHeader): Option[Future[SimpleResult]] = { Some(Future.successful(Unauthorized("No access"))) }

/** * Renders the index page. * * @returns The result to send to the client. */ def index = SecuredAction { implicit request => Ok(views.html.index(request.identity)) } }

Note: If you don’t implement one or both of the fallback methods, a 401 response with a simple message will be displayed to the user.

2.2.2 Adding Authorization

Silhouette provides a way to add authorization logic to your controller actions. This is done by implementing an Authorization object that is passed to 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 SecuredActions.

2.2. Actions 11 Silhouette Documentation, Release 2.0-RC1

* * @tparam I The type of the identity. */ trait Authorization[I <: Identity]{

/** * Checks whether the user is authorized to execute an action 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 action similar to the fallback for unauthenticated users.

Logic Operator

You can use logic operator (NOT AND OR) into your Authorization 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 }

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 action on which he isn’t au- thorized, the result specified in the global fallback method will be returned.

12 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

object Global extends GlobalSettings with SecuredSettings {

/** * Called when a user isn’t authorized. * * @param request The request header. * @param lang The current selected lang. * @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 notAuthorized. 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]{

/** * Called when a user isn’t authorized. * * @param request The request header. * @return The result to send to the client. */ override def notAuthorized(request: RequestHeader): Option[Future[SimpleResult]] = { 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.

2.2.3 Handle Ajax requests

Applications that accept both Ajax and normal requests should likely provide a JSON result to the first and a different result to others. There are two different approaches to achieve this. The first approach uses a non-standard HTTP request header. The Play application can check for this header and respond with a suitable result. The second approach uses Content negotiation to serve different versions of a document based on the ACCEPT request header.

2.2. Actions 13 Silhouette Documentation, Release 2.0-RC1

Non-standard header

The example below uses a non-standard HTTP request header inside a secured action and inside a fallback method for unauthenticated users. The JavaScript part with JQuery $.ajax({ headers:{ ’IsAjax’: ’true’}, ... });

The Play part with a local fallback method for unauthenticated users class Application(env: Environment[User, CookieAuthenticator]) extends Silhouette[User, CookieAuthenticator]{

/** * Called when a user isn’t authenticated. * * @param request The request header. * @return The result to send to the client. */ override def notAuthenticated(request: RequestHeader): Option[Future[SimpleResult]] = { val result = request.headers.get("IsAjax") match { case Some("true") => Json.obj("result" -> "No access") case_ => "No access" }

Some(Future.successful(Unauthorized(result))) }

/** * Renders the index page. * * @returns The result to send to the client. */ def index = SecuredAction { implicit request => val result = request.headers.get("IsAjax") match { case Some("true") => Json.obj("identity" -> request.identity) case_ => views.html.index(request.identity) }

Ok(result) } }

Content negotiation

By default Silhouette supports content negotiation for the most common media types: text/plain, text/html, application/ and application/xml. So if no local or global fallback methods are implemented, Sil- houette responds with the appropriate response based on the ACCEPT header defined by the user agent. The response format will default to plain text in case the request does not match one of the known media types. The example below uses content negotiation inside a secured action and inside a fallback method for unauthenticated users. The JavaScript part with JQuery

14 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

$.ajax({ headers:{ Accept: "application/json; charset=utf-8", "Content-Type": "application/json; charset=utf-8" }, ... })

The Play part with a local fallback method for unauthenticated users class Application(env: Environment[User, CookieAuthenticator]) extends Silhouette[User, CookieAuthenticator]{

/** * Called when a user isn’t authenticated. * * @param request The request header. * @return The result to send to the client. */ override def notAuthenticated(request: RequestHeader): Option[Future[SimpleResult]] = { val result = render{ case Accepts.Json() => Json.obj("result" -> "No access") case Accepts.Html() => "No access" }

Some(Future.successful(Unauthorized(result))) }

/** * Renders the index page. * * @returns The result to send to the client. */ def index = SecuredAction { implicit request => val result = render{ case Accepts.Json() => Json.obj("identity" -> request.identity) case Accepts.Html() => views.html.index(request.identity) } Ok(result) } }

2.3 Identity

Silhouette defines a user through its Identity trait. This trait doesn’t define any defaults. Thus, you are free to design your user according to your beliefs.

2.3.1 Login information

The LoginInfo acts as Silhouette’s identity ID and it’s needed to identify a user in the Silhouette workflow. It contains the data about the provider that authenticated an identity. This information is mostly publicly available and it simply consists of a unique provider ID and a unique key which identifies a user to this provider (userID, email, . . . ). If the application supports the concept of “merged identities”, i.e., the same user being able to authenticate through different providers, then make sure that the login information gets stored separately. Later you can see how this can

2.3. Identity 15 Silhouette Documentation, Release 2.0-RC1 be implemented.

2.3.2 Implement an identity

To define a user for your application, create a class that extends the Identity trait. Your user class may contain any information, without restriction. Below we define a simple user with a unique ID, the login information for the provider which authenticates that user and some basic information like name and email. case class User(, userID: Long, loginInfo: LoginInfo, name: String, email: Option[String]) extends Identity

2.3.3 The identity service

Silhouette relies on an implementation of IdentityService to handle all the operations related to retrieving identities. Using this delegation model means you are not forced to use a particular model object or persistence mechanism. Instead, you provide a service that translates back and forth between your models and what Silhouette understands. The IdentityService defines a raw type which must be derived from Identity. This has the advantage that your service implementation returns always your implementation of Identity. Here’s a sample implementation:

/** * A custom user service which relies on the previous defined ‘User‘. */ class UserService(userDAO: UserDAO) extends IdentityService[User]{

/** * Retrieves a user that matches the specified login info. * * @param loginInfo The login info to retrieve a user. * @return The retrieved user or None if no user could be retrieved for the given login info. */ def retrieve(loginInfo: LoginInfo): Future[Option[User]] = userDAO.findByLoginInfo(loginInfo) }

2.3.4 Link an identity to multiple login information

Silhouette doesn’t provide built-in functionality to link multiple identities to a single user, but it makes this task very easy by providing the basics. In the abstract this task can be done by linking the different login information returned by each provider, with a single user identified by an unique ID. The unique user will be represented by your implementation of Identity and the login information will be returned by every provider implementation after a successful authentication. Now with this basic knowledge it’s up to you to implement the linking in such a way that it fits into your application architecture.

16 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

2.4 Authenticator

An Authenticator is a key part of a Silhouette application, because it tracks a user after a successful authentication. The Authenticator itself is a small class which stores only some data like its validity and the linked login information for an identity.

2.4.1 Authenticator Service

Every Authenticator has an associated Authenticator Service which handles that kind of authenticator. This service is responsible for the following actions:

Create an authenticator

Creates a new authenticator for the login information of an identity. This step should be executed after a successful authentication. The created authenticator can then be used to track the user on every subsequent request.

Retrieve an authenticator

An authenticator service is used to check an incoming request for an existing instance of its authenticator. An au- thenticator can be embedded in any part of the incoming HTTP request (user session, cookie, user defined header field, request param). Some services can validate a retrieved authenticator, based on its ID, against a backing store. Silhouette automatically tries to retrieve an authenticator on every request to a Silhouette action.

Init an authenticator

Authenticators need to be initialized, usually when they are created during a successful authentication. If the service uses a backing store, then the authenticator instance will be stored in it, to check the validity of an authenticator on every subsequent request to the application.

Embed an authenticator

After initialization an authenticator must be embedded into a Play framework request or result. This can by done by creating a cookie, storing data into the user session or including the authenticator in a user defined header. Embedding the authenticator related data into the result means that the data will be sent to the client. It may also be useful to embed the authenticator related data into an incoming request to lead a Silhouette action to believe that the request is a new request which contains a valid authenticator. This is typically done in tests or Play filters.

Attention: The following actions are only used for internal purposes inside a Silhouette action. It’s provided here as background information and for advanced users.

Touch an authenticator

For authenticators that use a sliding window expiration, calling touch causes the last used time to be updated upon each request to a Silhouette action. Such updates are not needed for authenticators that do not use a sliding window expiration.

2.4. Authenticator 17 Silhouette Documentation, Release 2.0-RC1

Update an authenticator

Automatically updates the state of an authenticator on every request to a Silhouette action. Updated authenticators will be embedded into the Play framework result before sending it to the client. If the service uses a backing store, then the authenticator instance will be updated in the store, too.

Renew an authenticator

Authenticators have a fix expiration date. With this method it’s possible to renew the expiration of an authenticator by discarding the old one and creating a new one. Based on the implementation, the renew method revokes the given authenticator first, before creating a new one. If the authenticator was updated, then the updated artifacts will be embedded into the response.

Note: To renew an authenticator you must call the renew method of the authenticator instance inside a Silhouette action. This method accepts a Result and returns a wrapped Renew result, which notifies the action to renew the authenticator instead of updating it.

Discard an authenticator

Every request to a Silhouette action causes invalid authenticators to be automatically discarded. Discarding means that all client side stored artifacts will be removed. If the service uses a backing store, then the authenticator will also be removed from it. Logging a user out of a Silhouette application requires explicitly discarding the authenticator.

Note: To discard an authenticator you must call the discard method of the authenticator instance inside a Silhouette action. This method accepts a Result and returns a wrapped Discard result, which notifies the action to discard the authenticator instead of updating it.

2.4.2 List of authenticators

Silhouette comes with a set of stateless as well as stateful authenticator implementations that cover most use cases. It’s up to you to decide which authenticator fits best into your application architecture.

Hint: Good decision aids can be found in the blog posts Cookies vs Tokens. Getting auth right with Angular.JS and 10 Things You Should Know about Tokens from Auth0.

CookieAuthenticator

An authenticator that uses a stateful, cookie-based approach. It works by storing the unique ID of the authenticator in a cookie. This ID gets then mapped to an authenticator instance in the server side backing store. This approach could also be named “server side session”. The authenticator can use a sliding window expiration. This means that the authenticator times out after a certain time if it hasn’t been used. This can be controlled with the authenticatorIdleTimeout property of the settings class. Pros • Small network throughput on client side • Ideal for traditional browser based websites

18 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

• Client fingerprinting Cons • Larger network throughput on the server side • Not stateless (needs a synchronized backing store) • Less than ideal for mobile or single page apps • Can be vulnerable for CSRF attacks • Does not play well with CORS

Tip: Please take a look on the configuration settings, on how to configure this authenticator.

SessionAuthenticator

New in version 2.0. An authenticator that uses a stateless, session-based approach. It works by storing a serialized authenticator instance in the Play Framework session cookie. The authenticator can use a sliding window expiration. This means that the authenticator times out after a certain time if it hasn’t been used. This can be controlled with the authenticatorIdleTimeout property of the settings class. Pros • No network throughput on the server side • Ideal for traditional browser based websites • Client fingerprinting • Stateless Cons • Larger network throughput on client side • Less than ideal for mobile or single page apps • Can be vulnerable for CSRF attacks • Does not play well with CORS

Tip: Please take a look on the configuration settings, on how to configure this authenticator.

BearerTokenAuthenticator

New in version 2.0. An authenticator that uses a header-based approach with the help of a bearer token. It works by transporting a token in a user defined header to track the authenticated user and a server side backing store that maps the token to an authenticator instance. The authenticator can use a sliding window expiration. This means that the authenticator times out after a certain time if it hasn’t been used. This can be controlled with the authenticatorIdleTimeout property of the settings class. Pros • Small network throughput on client side

2.4. Authenticator 19 Silhouette Documentation, Release 2.0-RC1

• Ideal for mobile or single page apps • Not vulnerable against CSRF attacks • Plays well with CORS Cons • Larger network throughput on the server side • Not stateless (needs a synchronized backing store) • Less than ideal for traditional browser based websites • No client fingerprinting

Tip: Please take a look on the configuration settings, on how to configure this authenticator.

JWTAuthenticator

New in version 2.0. An authenticator that uses a header-based approach with the help of a JWT (JSON Web Token). It works by using a JWT to transport the authenticator data inside a user defined header. It can be stateless with the disadvantages that the JWT can’t be invalidated. The authenticator can use a sliding window expiration. This means that the authenticator times out after a certain time if it hasn’t been used. This can be controlled with the authenticatorIdleTimeout property of the settings class. If this feature is activated then a new token will be generated on every update. Make sure your application can handle this case. Pros • Ideal for mobile or single page apps • Can be stateless (with the disadvantages it can’t be invalidated) • Not vulnerable against CSRF attacks • Plays well with CORS Cons • Larger network throughput on client side • Larger network throughput on the server side (if backing store is used) • Less than ideal for traditional browser based websites • No client fingerprinting

Tip: Please take a look on the configuration settings, on how to configure this authenticator.

DummyAuthenticator

New in version 2.0. An authenticator that can be used if a client doesn’t need an authenticator to track a user. This can be useful for request providers, because authentication may occur here on every request to a protected resource. Pros

20 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

• Ideal for request providers • Doesn’t have any network throughput or memory footprint compared to other authenticators Cons • Cannot track users • Only useful for request providers

2.5 Providers

In Silhouette a provider is a service that handles the authentication of an identity. It typically reads authorization information and returns information about an identity.

2.5.1 Request providers

Request providers are special types of providers. They can be hooked directly into incoming requests which gets scanned for credentials and either gain or restrict access to the protected resources. This can be useful for machine authentication. When using a request provider, you should consider using the dummy authenticator, because it doesn’t have any network throughput or memory footprint compared to other authenticators.

2.5.2 Credentials provider

Silhouette supports local authentication, typically via an HTML form, with the credentials provider. This provider accepts credentials and returns the login information for an identity after a successful authentication. Typically cre- dentials consist of an identifier (a username or email address) and a password. The credentials provider supports changing the password hashing algorithm on the fly. Sometimes it may be possible to change the hashing algorithm used by the application. But the hashes stored in the backing store can’t be converted back into plain text passwords to hash them again with the new algorithm. So if a user successfully authenticates after the application has changed the hashing algorithm, the provider hashes the entered password again with the new algorithm and stores the authentication info in the backing store.

2.5.3 Basic Authentication provider

Silhouette supports basic access authentication as described in RFC 2617. This provider is an implementation of a request provider which accepts the current request and returns the login information for an identity after a successful authentication. The basic authentication provider supports changing the password hashing algorithm on the fly. Sometimes it may be possible to change the hashing algorithm used by the application. But the hashes stored in the backing store can’t be converted back into plain text passwords to hash them again with the new algorithm. So if a user successfully authenticates after the application has changed the hashing algorithm, the provider hashes the entered password again with the new algorithm and stores the authentication info in the backing store.

2.5.4 Social providers

A social provider allows a user to authenticate an identity on your website with an existing account from an external social website like Facebook, Google or Twitter. Following you can find a list of all supported providers grouped by authentication protocol.

2.5. Providers 21 Silhouette Documentation, Release 2.0-RC1

OAuth1

• LinkedInProvider (www.linkedin.com) • TwitterProvider (www.twitter.com) • XingProvider (www.xing.com)

OAuth2

• ClefProvider (getclef.com) • DropboxProvider (www.dropbox.com) • FacebookProvider (www.facebook.com) • FoursquareProvider (www.foursquare.com) • GitHubProvider (www.github.com) • GoogleProvider (www.google.com) • InstagramProvider (www.instagram.com) • LinkedInProvider (www.linkedin.com) • VKProvider (www.vk.com)

OpenID

• SteamProvider (www.steamcommunity.com) • YahooProvider (www.yahoo.com)

2.5.5 Social profile

The social profile contains the profile data returned from the social providers. Silhouette provides a default social profile called CommonSocialProfile, which contains the most common profile data providers return.

2.5.6 Social profile builders and parsers

Some providers return a superset of the information in CommonSocialProfile, for example the location or gender of the user. Silouette’s “profile builders” allow you to construct custom profiles without duplicating existing code. Instead of overriding the provider to return the additional profile information, developers can mix in a profile builder which adds only the programming logic for the additional fields. Every profile builder must define a profile parser, which transforms the content returned from the provider, into a social profile instance. Parsers can then be reused by other parsers to avoid code duplication.

Default social profile builders

All provider implementations are abstract because a profile builder must be specified before an object can be instanti- ated. Every provider ships with a default profile builder, an implementation of CommonSocialProfileBuilder, which returns a CommonSocialProfile after successful authentication. However, it can’t be mixed into the provider by default because Scala doesn’t allow us to override a concrete type with a different concrete type. That is, mixing in a default builder would prevent customization.

22 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

Silhouette handles this problem by leaving the provider class abstract but making the companion ob- ject’s apply method handle the most common case – instantiating a provider with an implementation of CommonSocialProfileBuilder. So in most cases you should simply call the apply method and instead of using the new keyword: FacebookProvider(httpLayer, stateProvider, settings)

Write a custom social profile builder

As noted above it is very easy to write your own profile builder implementations. Let’s take a look on the following code examples. The first one defines a custom social profile that differs from the common social profile by the additional gender field. case class CustomSocialProfile( loginInfo: LoginInfo, firstName: Option[String] = None, lastName: Option[String] = None, fullName: Option[String] = None, email: Option[String] = None, avatarURL: Option[String] = None, gender: Option[String] = None) extends SocialProfile

As next we create the parser which uses the default Facebook profile parser to avoid code duplication. class CustomFacebookProfileParser extends SocialProfileParser[JsValue, CustomSocialProfile]{

/** * The common social profile parser. */ val commonParser = new FacebookProfileParser

/** * Parses the social profile. * * @param json The content returned from the provider. * @return The social profile from given result. */ def parse(json: JsValue) = commonParser.parse(json).map{ commonProfile => val gender = (json\ "gender").as[ String] CustomSocialProfile( loginInfo = commonProfile.loginInfo, firstName = commonProfile.firstName, lastName = commonProfile.lastName, fullName = commonProfile.fullName, avatarURL = commonProfile.avatarURL, email = commonProfile.email, gender = Some(gender)) } }

As you can see there is no need to duplicate any Json parsing. The only thing to do is to query the gender field from the Json response returned by the Facebook API. As last we create a profile builder which can then be mixed into the Facebook provider to return our previously defined custom profile. trait CustomFacebookProfileBuilder { self: FacebookProvider =>

2.5. Providers 23 Silhouette Documentation, Release 2.0-RC1

/** * The type of the profile a profile builder is responsible for. */ type Profile = CustomSocialProfile

/** * The profile parser. */ val profileParser = new CustomFacebookProfileParser }

Now you can mixin the profile builder by instantiating the Facebook provider with the profile builder. new FacebookProvider(httpLayer, stateProvider, settings) with CustomFacebookProfileBuilder

2.5.7 OAuth1 token secret

New in version 2.0. During a typical OAuth1 flow, a provider retrieves a request token in conjunction with a token secret. This token secret is then needed to create the signature for retrieving the access token from the OAuth1 provider. Due the fact that we leave our application during the OAuth flow to authorize against the provider, we must cache the token secret so that is available to obtain the access token, after redirecting back to our application. To maintain the token secret in Silhouette, a token secret provider must be passed to every OAuth1 authentication provider. All token secret provider implementations can be found in the secrets package.

List of OAuth1 token secret providers

We provide some built in token secret providers.

CookieSecret

The cookie secret works by embedding an encrypted token secret in a cookie. This provides a stateless/scalable approach.

Tip: Please take a look on the configuration settings, on how to configure the provider for this secret.

2.5.8 OAuth2 state

New in version 2.0. The OAuth2 protocol supports the state parameter, a value the client can include in the request and that the server returns as a parameter unmodified in the response. This parameter should be used mainly to protect an application against CSRF attacks. But it can also be used to remember some state about the user. To maintain the state in Silhouette, a state provider must be passed to every OAuth2 authentication provider. All state provider implementations can be found in the state package.

24 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

List of OAuth2 states

We provide some built in state providers. But as noted above a customized state can be implemented to remember some state about a user.

CookieState

The cookie state works by embedding the state in a cookie. This is one of the preferred methods from the OAuth2 RFC and it provides a stateless/scalable approach.

Tip: Please take a look on the configuration settings, on how to configure the provider for this state.

DummyState

The dummy state can be used to avoid state validation. This can be useful if the state should be validated on client side.

2.5.9 Request extractors

New in version 2.0. The default workflow for traditional web applications is it to send values in URL query parameters, but for mobile applications there could be another workflow. With request extractors it’s possible to extract values sent to the client in different parts of the request. By default Silhouette can read values from query parameters and from a request body containing form-urlencoded, Json or XML data. For example, if a parameter with the name code is needed by Silhouette inside a provider, then the parameter could be sent in the following parts of the request: URL Query Parameter ?code=value

Form URL encoded body code=value

Json body {"code": "value"}

XML body value

Note: Parameters sent as query parameters always have precedence over parameters sent in the body of a request. So if a parameter is sent in query and in body, then the query parameter wins.

Define custom request extractors

It is possible to define custom request extractors by providing an implicit RequestExtractor implementation.

2.5. Providers 25 Silhouette Documentation, Release 2.0-RC1

2.5.10 Authentication information

The AuthInfo implementation contains authentication information such as access tokens, hashed passwords, and so on – which should never be exposed to the public. Each provider defines its own AuthInfo implementation. As with other Silhouette structures that vary in their implementation, AuthInfo is managed by an AuthInfoService that saves and retrieves the information as needed.

2.6 Error handling

Every provider implementation in Silhouette throws either an AccessDeniedException or an AuthenticationException. The AccessDeniedException will be thrown if a social provider de- nies an authentication attempt. In any other case the AuthenticationException will be thrown. Due to the asynchronous nature of Silhouette, all exceptions will be thrown in Futures. This means that exceptions may not be caught in a try catch block because Futures may be executed in separate Threads. To solve this problem, Futures have their own error handling mechanism. They can be recovered from an error state into desirable state with the help of the recover and recoverWith methods. Both methods accept a partial function which transforms the Exception into any other type. For more information please visit the Future trait’s documentation. All controller implementations which are derived from the Silhouette base controller can use the exceptionHandler method. This is a default recover implementation which transforms an AccessDeniedException into a 403 Forbidden result and an AuthenticationException into a 401 Unauthorized result. The following code snippet shows how an error can be recovered from a failed authentication. authenticate().map{ result => // Do something with the result }.recoverWith(exceptionHandler)

Note: The exceptionHandler method calls the local or global fallback methods under the hood to return the appropriate result.

It is also possible to override the exception handler to handle user defined exceptions and to delegate the errors to the expected status codes.

2.7 Caching

Silhouette caches some authentication artifacts. Here are some suggestions on configuring or implementing caching in your application architecture so that Silhouette can also use it.

2.7.1 Implement caching

By default, Play is shipped with EHCache, a lightweight, in-memory cache. If this is not enough for your application architecture, there are at least two other caching options to use with Silhouette.

Use another Play cache plugin

Play has its own cache plugin architecture. So the easiest approach is to use another cache plugin for Play. You can then use the PlayCacheLayer implementation and plug a new cache into your application.

26 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

Implement your own cache layer

Silhouette provides a CacheLayer trait which can be used to create a custom cache implementation.

2.7.2 Clustered environment

If you use a clustered environment for your application then make sure that you use a distributed cache like Redis or Memcached. Otherwise cached artifacts must be synchronized between instances.

2.7.3 Development mode

When using the default Play cache in development mode, the cache will be cleaned after every app reload. This is because Play’s cache (EHCache) is configured to store the data only im memory by default. This can be changed by overriding the shipped ehcache.xml from the jars to persist the cache on the disk. To change the default behaviour you must copy the ehcache.xml from the distribution jars to your configuration directory. Then add and change diskPersistent to true. The following example shows a possible configuration. Adapt it to fit your needs.

Note: You can also get around this issue by using a distributed cache like Redis or Memcached.

2.8 Events

Silhouette provides event handling based on Akka’s Event Bus. The following events are provided by Silhouette, although only the three marked events are fired from core. • SignUpEvent • LoginEvent • LogoutEvent • AccessDeniedEvent • AuthenticatedEvent * • NotAuthenticatedEvent *

2.8. Events 27 Silhouette Documentation, Release 2.0-RC1

• NotAuthorizedEvent * It is very easy to propagate your own events over the event bus by implementing the SilhouetteEvent trait. case class CustomEvent() extends SilhouetteEvent

2.8.1 Use the event bus

The event bus is available in every Silhouette controller using the environment variable env.eventBus. You can also inject the event bus into other classes like services or DAOs. EventBus() is a singleton you can call to ensure that you always use the same event bus.

2.8.2 Listen for events

To listen for events you must implement a listener based on an Actor and then register the listener, with the event to listen, on the event bus instance: val listener = system.actorOf(Props(new Actor { def receive = { case e @ LoginEvent(identity: User, request, lang) => println(e) case e @ LogoutEvent(identity: User, request, lang) => println(e) } })) val eventBus = EventBus() eventBus.subscribe(listener, classOf[LoginEvent[User]]) eventBus.subscribe(listener, classOf[LogoutEvent[User]])

2.8.3 Publish events

Publishing events is also simple: val eventBus = EventBus() eventBus.publish(LoginEvent[User](identity, request, lang))

2.9 Logging

Silhouette uses named loggers for logging. This gives your application fine-grained control over the log entries logged by Silhouette.

2.9.1 Configure logging

Note: Play uses Logback as its logging framework. Please see the Play documentation on how to configure base logging in Play.

As an example, in your logging.xml you can set the logging level for the complete com.mohiva namespace to ERROR and then define a more detailed level for some components.

28 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

... ...

2.9.2 Use the logger

To use the named logger you only need to mix the Logger trait into your class or trait. Then you can use the logger property to access the Play logging API. import com.mohiva.play.silhouette.core.Logger

class LoggerExample extends Logger { def log{ logger.error("My log message") } }

2.10 Testing

New in version 2.0.

2.10.1 Test Helpers

Silhouette provides some test helpers that can be used to easily test your Silhouette application. These helpers are located in the additional TestKit dependency. libraryDependencies ++= Seq( "com.mohiva"%% "play-silhouette-testkit"% "version"% "test" )

After providing the dependency, the helpers can be used by importing the following package into your test scenarios. import com.mohiva.play.silhouette.test._

All helpers are test framework agnostic. You can use it with Specs2, ScalaTest or every other testing framework. In our examples we use Specs2 to demonstrate how to test Silhouette applications, because it’s the default framework shipped with Play and we use it to test Silhouette itself.

2.10.2 Test Silhouette actions

With the previous mentioned test helpers it’s really easy to test your Silhouette actions. As first lets look how a typical controller instance could look like: class UserController( val env: Environment[User, CookieAuthenticator]) extends Silhouette[User, CookieAuthenticator]{

/** * Gets a user.

2.10. Testing 29 Silhouette Documentation, Release 2.0-RC1

*/ def user = SecuredAction { implicit request => Ok(Json.toJson(request.identity)) }

/** * Checks if a user is authenticated. */ def isAuthenticated = UserAwareAction { implicit request => request.identity match { case Some(identity) => Ok case None => Unauthorized } } }

If you would like to test this controller, you must provide an environment that can handle your User implementation and the CookieAuthenticator which is used to track your user after a successful authentication. For this case Silhouette provides a FakeEnvironment which automatically sets up all components needed to test your specific actions.

FakeEnvironment

The fake environment does all the annoying steps for you to create and instantiate all dependencies that you need for your test. You must only specify one or more LoginInfo -> Identity pairs that should be returned by calling request.identity in your action and the authenticator instance that tracks this user. val identity = User(LoginInfo("facebook", "[email protected]")) implicit val env = FakeEnvironment[User, CookieAuthenticator](Seq(identity.loginInfo -> identity))

Under the hood, the environment instantiates a FakeIdentityService which stores your given identities and returns it if needed. It instantiates also the appropriate AuthenticatorService based on your defined Authenticator type. All Authenticator services are real authenticator service instances set up with their default values and dependencies.

FakeRequest

Let us summarize briefly how an Action in Play works. An action is basically an anonymous function that handles a Request and returns a Result. With this in mind we must now create a request that we can pass to our action. Play ships with a test helper called FakeRequest which does exactly what we want. But this helper cannot embed an authenticator into the created fake request. Therefore we have extend Play’s fake request helper with two additional methods. The first method accepts an authenticator instance which is then embedded into the request. val identity = User(LoginInfo("facebook", "[email protected]")) implicit val env = FakeEnvironment[FakeIdentity, CookieAuthenticator](Seq(identity.loginInfo -> identity)) val authenticator = new CookieAuthenticator("test", identity.loginInfo,...) val request = FakeRequest().withAuthenticator(authenticator)

The second method accepts a LoginInfo instance for which an authenticator will be created and embedded into the request. val identity = User(LoginInfo("facebook", "[email protected]")) implicit val env = FakeEnvironment[FakeIdentity, CookieAuthenticator](Seq(identity.loginInfo -> identity)) val request = FakeRequest().withAuthenticator(identity.loginInfo)

30 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

Note: To embed an authenticator into a request you need an implicit environment in scope.

Tying the Pieces Together

So far, we’ve learned how to setup a test environment and how to create a request which contains an embedded authenticator. Now we combine these techniques and create a complete controller test.

Simulate a missing authenticator

To simulate that an authenticator couldn’t be found for a request, you must only submit a request without an authenti- cator. class UserSpec extends PlaySpecification {

"The ‘user‘ method" should{ "return status 401 if no authenticator was found" in new WithApplication { val identity = User(LoginInfo("facebook", "[email protected]")) val env = FakeEnvironment[User, CookieAuthenticator](Seq(identity.loginInfo -> identity)) val request = FakeRequest()

val controller = new UserController(env) val result = controller.user(request)

status(result) must equalTo(UNAUTHORIZED) } }

"The ‘isAuthenticated‘ method" should{ "return status 401 if no authenticator was found" in new WithApplication { val identity = User(LoginInfo("facebook", "[email protected]")) val env = FakeEnvironment[User, CookieAuthenticator](Seq(identity.loginInfo -> identity)) val request = FakeRequest()

val controller = new UserController(env) val result = controller.isAuthenticated(request)

status(result) must equalTo(UNAUTHORIZED) } } }

Simulate a missing identity

To simulate that an identity couldn’t be found for a valid authenticator, you must pass different login information to the user and the authenticator. class UserSpec extends PlaySpecification {

"The ‘user‘ method" should{ "return status 401 if authenticator but no identity was found" in new WithApplication { val identity = User(LoginInfo("facebook", "[email protected]")) implicit val env = FakeEnvironment[User, CookieAuthenticator](Seq(identity.loginInfo -> identity)) val request = FakeRequest()

2.10. Testing 31 Silhouette Documentation, Release 2.0-RC1

.withAuthenticator(LoginInfo("xing", "[email protected]"))

val controller = new UserController(env) val result = controller.user(request)

status(result) must equalTo(UNAUTHORIZED) } }

"The ‘isAuthenticated‘ method" should{ "return status 401 if authenticator but no identity was found" in new WithApplication { val identity = User(LoginInfo("facebook", "[email protected]")) implicit val env = FakeEnvironment[User, CookieAuthenticator](Seq(identity.loginInfo -> identity)) val request = FakeRequest() .withAuthenticator(LoginInfo("xing", "[email protected]"))

val controller = new UserController(env) val result = controller.isAuthenticated(request)

status(result) must equalTo(UNAUTHORIZED) } } }

Simulate an authenticated identity

To simulate an authenticated identity we must submit a valid authenticator and the login information of both the authenticator and the identity must be the same. class UserSpec extends PlaySpecification {

"The ‘user‘ method" should{ "return status 200 if authenticator and identity was found" in new WithApplication { val identity = User(LoginInfo("facebook", "[email protected]")) implicit val env = FakeEnvironment[User, CookieAuthenticator](Seq(identity.loginInfo -> identity)) val request = FakeRequest().withAuthenticator(identity.loginInfo)

val controller = new UserController(env) val result = controller.user(request)

status(result) must equalTo(OK) } }

"The ‘isAuthenticated‘ method" should{ "return status 200 if authenticator and identity was found" in new WithApplication { val identity = User(LoginInfo("facebook", "[email protected]")) implicit val env = FakeEnvironment[User, CookieAuthenticator](Seq(identity.loginInfo -> identity)) val request = FakeRequest().withAuthenticator(identity.loginInfo)

val controller = new UserController(env) val result = controller.isAuthenticated(request)

status(result) must equalTo(OK) } } }

32 Chapter 2. How it works Silhouette Documentation, Release 2.0-RC1

2.10.3 Test default Play actions

Typically Silhouette authentication code is implemented inside default Play actions. To test such actions you don’t need specific helper classes. Here you could use Mockito to mock the Silhouette instances or other related testing tools.

2.10. Testing 33 Silhouette Documentation, Release 2.0-RC1

34 Chapter 2. How it works CHAPTER 3

Configuration

3.1 Configuration

3.1.1 Introduction

Silhouette doesn’t dictate the form and the location of the configuration. So you are able to configure it over a database, a configuration server or the Play Framework configuration mechanism. Generally, configuration in Silhouette is handled over configuration objects. These objects must be filled with data and then passed to the instance to configure.

Note: For reasons of simplification we use the Play Framework configuration syntax in our examples.

3.1.2 OAuth1 based providers

To configure OAuth1 based providers you must use the OAuth1Settings class. This class has the following form: case class OAuth1Settings( requestTokenURL: String, accessTokenURL: String, authorizationURL: String, callbackURL: String, consumerKey: String, consumerSecret: String)

Property Description requestTokenURL The request token URL provided by the OAuth provider accessTokenURL The access token URL provided by the OAuth provider authorizationURL The authorization URL provided by the OAuth provider callbackURL The callback URL to the application after a successful authentication on the OAuth provider consumerKey The consumer ID provided by the OAuth provider consumerSecret The consumer secret provided by the OAuth provider

Callback URL

The callbackURL must point to your action which is responsible for the authentication over your defined providers. So if you define the following route as example:

35 Silhouette Documentation, Release 2.0-RC1

GET /authenticate/:provider @controllers.SocialAuthController.authenticate(provider)

Then your callbackURL must have the following format: callbackURL="https://your.domain.tld/authenticate/linkedin"

Example

Your configuration could then have this format: linkedin { requestTokenURL="https://api.linkedin.com/uas/oauth/requestToken" accessTokenURL="https://api.linkedin.com/uas/oauth/accessToken" authorizationURL="https://api.linkedin.com/uas/oauth/authenticate" callbackURL="https://your.domain.tld/authenticate/linkedin" consumerKey="your.consumer.key" consumerSecret="your.consumer.secret" } twitter { requestTokenURL="https://twitter.com/oauth/request_token" accessTokenURL="https://twitter.com/oauth/access_token" authorizationURL="https://twitter.com/oauth/authenticate" callbackURL="https://your.domain.tld/authenticate/twitter" consumerKey="your.consumer.key" consumerSecret="your.consumer.secret" } xing { requestTokenURL="https://api.xing.com/v1/request_token" accessTokenURL="https://api.xing.com/v1/access_token" authorizationURL="https://api.xing.com/v1/authorize" callbackURL="https://your.domain.tld/authenticate/xing" consumerKey="your.consumer.key" consumerSecret="your.consumer.secret" }

To get the consumerKey/consumerSecret keys you need to log into the developer site of each service and register your application.

3.1.3 OAuth1 token secret

CookieSecret

To configure the CookieSecret provider you must use the CookieSecretSettings class. This class has the following form: case class CookieSecretSettings( cookieName: String = "OAuth1TokenSecret", cookiePath: String = "/", cookieDomain: Option[String] = None, secureCookie: Boolean = Play.isProd, httpOnlyCookie: Boolean = true, expirationTime: Int =5 * 60)

36 Chapter 3. Configuration Silhouette Documentation, Release 2.0-RC1

Property Description cookieName The cookie name cookiePath The cookie path cookieDomainThe cookie domain secureCookieWhether this cookie is secured, sent only for HTTPS requests. Default to sending only for HTTPS in production, but not for development and test httpOnlyCookieWhether this cookie is HTTP only, i.e. not accessible from client-side JavaScript code expirationTimeSecret expiration. Defaults to 5 minutes which provides sufficient time to log in, but not too much. This is a balance between convenience and security

3.1.4 OAuth2 based providers

To configure OAuth2 based providers you must use the OAuth2Settings class. This class has the following form: case class OAuth2Settings( authorizationURL: Option[String], accessTokenURL: String, redirectURL: String, clientID: String, clientSecret: String, scope: Option[String] = None, authorizationParams: Map[String, String] = Map(), accessTokenParams: Map[String, String] = Map(), customProperties: Map[String, String] = Map())

Property Description authorizationURLThe authorization URL provided by the OAuth provider. This isn’t needed when using Silhouette in conjunction with client side authentication frameworks accessTokenURLThe access token URL provided by the OAuth provider redirectURL The redirect URL to the application after a successful authentication on the OAuth provider clientID The client ID provided by the OAuth provider clientSecret The client secret provided by the OAuth provider scope The OAuth2 scope parameter provided by the OAuth provider authorizationParamsAdditional params to add to the authorization request accessTokenParamsAdditional params to add to the access token request customPropertiesA map of custom properties for the different providers

Redirect URL

The redirectURL must point to your action which is responsible for the authentication over your defined providers. So if you define the following route as example: GET /authenticate/:provider @controllers.SocialAuthController.authenticate(provider)

Then your redirectURL must have the following format: redirectURL="https://your.domain.tld/authenticate/facebook"

Example

Your configuration could then have this format:

3.1. Configuration 37 Silhouette Documentation, Release 2.0-RC1

clef { accessTokenUrl="https://clef.io/api/v1/authorize" redirectURL="https://your.domain.tld/authenticate/clef" clientId="your.client.id" clientSecret="your.client.secret" } dropbox { authorizationUrl="https://www.dropbox.com/1/oauth2/authorize" accessTokenUrl="https://api.dropbox.com/1/oauth2/token" redirectURL="https://your.domain.tld/authenticate/dropbox" clientId="your.client.id" clientSecret="your.client.secret" } facebook { authorizationUrl="https://graph.facebook.com/oauth/authorize" accessTokenUrl="https://graph.facebook.com/oauth/access_token" redirectURL="https://your.domain.tld/authenticate/facebook" clientId="your.client.id" clientSecret="your.client.secret" scope=email } foursquare { authorizationUrl="https://foursquare.com/oauth2/authenticate" accessTokenUrl="https://foursquare.com/oauth2/access_token" redirectURL="https://your.domain.tld/authenticate/foursquare" clientId="your.client.id" clientSecret="your.client.secret" } { authorizationUrl="https://github.com/login/oauth/authorize" accessTokenUrl="https://github.com/login/oauth/access_token" redirectURL="https://your.domain.tld/authenticate/github" clientId="your.client.id" clientSecret="your.client.secret" } google { authorizationUrl="https://accounts.google.com/o/oauth2/auth" accessTokenUrl="https://accounts.google.com/o/oauth2/token" redirectURL="https://your.domain.tld/authenticate/google" clientId="your.client.id" clientSecret="your.client.secret" scope="profile email" } instagram { authorizationUrl="https://api.instagram.com/oauth/authorize" accessTokenUrl="https://api.instagram.com/oauth/access_token" redirectURL="https://your.domain.tld/authenticate/instagram" clientId="your.client.id" clientSecret="your.client.secret" } linkedin {

38 Chapter 3. Configuration Silhouette Documentation, Release 2.0-RC1

authorizationUrl="https://www.linkedin.com/uas/oauth2/authorization" accessTokenUrl="https://www.linkedin.com/uas/oauth2/accessToken" redirectURL="https://your.domain.tld/authenticate/linkedin" clientId="your.client.id" clientSecret="your.client.secret" } vk { authorizationUrl="http://oauth.vk.com/authorize" accessTokenUrl="https://oauth.vk.com/access_token" redirectURL="https://your.domain.tld/authenticate/vk" clientId="your.client.id" clientSecret="your.client.secret" scope="email" }

To get the clientId/clientSecret keys you need to log into the developer site of each service and register your application.

3.1.5 OAuth2 state

CookieState

To configure the CookieState provider you must use the CookieStateSettings class. This class has the following form: case class CookieStateSettings( cookieName: String = "OAuth2State", cookiePath: String = "/", cookieDomain: Option[String] = None, secureCookie: Boolean = Play.isProd, httpOnlyCookie: Boolean = true, expirationTime: Int =5 * 60) Property Description cookieName The cookie name cookiePath The cookie path cookieDomainThe cookie domain secureCookieWhether this cookie is secured, sent only for HTTPS requests. Default to sending only for HTTPS in production, but not for development and test httpOnlyCookieWhether this cookie is HTTP only, i.e. not accessible from client-side JavaScript code expirationTimeState expiration. Defaults to 5 minutes which provides sufficient time to log in, but not too much. This is a balance between convenience and security

3.1.6 OpenID based providers

To configure OpenID based providers you must use the OpenIDSettings class. This class has the following form: case class OpenIDSettings( providerURL: String, callbackURL: String, axRequired: Seq[(String, String)] = Seq.empty, axOptional: Seq[(String, String)] = Seq.empty, realm: Option[String] = None)

3.1. Configuration 39 Silhouette Documentation, Release 2.0-RC1

Property Description providerURL The OpenID provider URL used if no openID was given callbackURL The callback URL to the application after a successful authentication on the OpenID provider. axRequired Required attributes to return from the provider after a successful authentication axOptional Optional attributes to return from the provider after a successful authentication realm An URL pattern that represents the part of URL-space for which an OpenID Authentication request is valid

Callback URL

The callbackURL must point to your action which is responsible for the authentication over your defined providers. So if you define the following route as example: GET /authenticate/:provider @controllers.SocialAuthController.authenticate(provider)

Then your callbackURL must have the following format: callbackURL="https://your.domain.tld/authenticate/yahoo"

Example

Your configuration could then have this format: steam { providerURL="https://steamcommunity.com/openid/" callbackURL="https://your.domain.tld/authenticate/steam" realm="https://your.domain.tld" } yahoo { providerURL="https://me.yahoo.com/" callbackURL="https://your.domain.tld/authenticate/yahoo" axRequired={ "fullname": "http://axschema.org/namePerson", "email": "http://axschema.org/contact/email", "image": "http://axschema.org/media/image/default" } realm="https://your.domain.tld" }

3.1.7 Authenticators

CookieAuthenticator

To configure the CookieAuthenticator service you must use the CookieAuthenticatorSettings class. This class has the following form: case class CookieAuthenticatorSettings( cookieName: String = "id", cookiePath: String = "/", cookieDomain: Option[String] = None, secureCookie: Boolean = Play.isProd, httpOnlyCookie: Boolean = true, useFingerprinting: Boolean = true,

40 Chapter 3. Configuration Silhouette Documentation, Release 2.0-RC1

cookieMaxAge: Option[Int] = Some(12 * 60 * 60), authenticatorIdleTimeout: Option[Int] = Some(30 * 60), authenticatorExpiry: Int = 12 * 60 * 60) Property Description cookieName The cookie name cookiePath The cookie path cookieDomain The cookie domain secureCookie Whether this cookie is secured, sent only for HTTPS requests. Default to sending only for HTTPS in production, but not for development and test httpOnlyCookie Whether this cookie is HTTP only, i.e. not accessible from client-side JavaScript code useFingerprintingIndicates if a fingerprint of the user should be stored in the authenticator cookieMaxAge The cookie expiration date in seconds, None for a transient cookie. Defaults to 12 hours authenticatorIdleTimeoutThe time in seconds an authenticator can be idle before it timed out. Defaults to 30 minutes authenticatorExpiryThe expiry of the authenticator in seconds. Defaults to 12 hours

SessionAuthenticator

To configure the SessionAuthenticator service you must use the SessionAuthenticatorSettings class. This class has the following form: case class SessionAuthenticatorSettings( sessionKey: String = "authenticator", encryptAuthenticator: Boolean = true, useFingerprinting: Boolean = true, authenticatorIdleTimeout: Option[Int] = Some(30 * 60), authenticatorExpiry: Int = 12 * 60 * 60) Property Description sessionKey The key of the authenticator in the session encryptAuthenticator Indicates if the authenticator should be encrypted in session useFingerprinting Indicates if a fingerprint of the user should be stored in the authenticatorIdleTimeoutThe time in seconds an authenticator can be idle before it timed out. Defaults to 30 minutes authenticatorExpiry The expiry of the authenticator in seconds. Defaults to 12 hours

BearerTokenAuthenticator

To configure the BearerTokenAuthenticator service you must use the BearerTokenAuthenticatorSettings class. This class has the following form: case class BearerTokenAuthenticatorSettings( headerName: String = "X-Auth-Token", authenticatorIdleTimeout: Option[Int] = Some(30 * 60), authenticatorExpiry: Int = 12 * 60 * 60) Property Description headerName The name of the header in which the token will be transfered authenticatorIdleTimeoutThe time in seconds an authenticator can be idle before it timed out. Defaults to 30 minutes authenticatorExpiry The expiry of the authenticator in seconds. Defaults to 12 hours

3.1. Configuration 41 Silhouette Documentation, Release 2.0-RC1

JWTAuthenticator

To configure the JWTAuthenticator service you must use the JWTAuthenticatorSettings class. This class has the following form: case class JWTAuthenticatorSettings( headerName: String = "X-Auth-Token", issuerClaim: String = "play-silhouette", encryptSubject: Boolean = true, authenticatorIdleTimeout: Option[Int] = None, authenticatorExpiry: Int = 12 * 60 * 60, sharedSecret: String)

Property Description headerName The name of the header in which the token will be transfered issuerClaim The issuer claim identifies the principal that issued the JWT encryptSubject Indicates if the subject should be encrypted in JWT authenticatorIdleTimeoutThe time in seconds an authenticator can be idle before it timed out. This feature is disabled by default to prevent the generation of a new JWT on every request authenticatorExpiryThe expiry of the authenticator in seconds. Defaults to 12 hours sharedSecret The shared secret to sign the JWT

42 Chapter 3. Configuration