Persistence

Silhouette is very loosely coupled to the persistence layer. In the next sections you will see, how you can implement your persistence layer and we show you some tools, provided by Silhouette, that can help to keep this implementation as simple as necessary.

Introduction

Silhouette has only two points where it needs to read or write data from an underlying persistence layer:

Authenticators

Some authenticators have the need to persistent either the complete authenticator or parts of the authenticator in a backing store. These kind of authenticators have a dependency to the AuthenticatorRepository which provides a well defined interface, that can be implemented to persist the authenticator related data into any kind of data storage.

Providers

Currently the password based providers like the BasicAuthProvider or the CredentialsProvider need the ability to read the PasswordInfo from a persistence layer during authentication. These both providers support also the change of password hashing algorithms on the fly. So they need a mechanism to update the PasswordInfo in the backing store. These kind of providers have a dependency to the AuthInfoRepository, which provides a well defined interface, that can be implemented to persist the AuthInfo into any kind of data storage.

It can also make sense to store the AuthInfo for the social providers, so that it's possible to call the provider API again to fetch additional data. For these kind of providers you can also use the AuthInfoRepository to store the related AuthInfo. But because this is an optional step, there is no binding to the Silhouette core. So if you have the need to store this information, you must inject an instance of the AuthInfoRepository into your controller or service and then call the appropriate methods to handle the AuthInfo.

Usage

It's possible to provide concrete implementations of the AuthenticatorRepository and the AuthInfoRepository without any additional dependency, because both traits come with the core of Silhouette. So if you have implemented your concrete class, you must only bind it with your preferred dependency injection framework.

We provide also default implementations of both traits, which can be used easily, if you add the following dependency to your SBT build file.

libraryDependencies ++= Seq(
  "com.mohiva" %% "play-silhouette-persistence" % "4.0.0"
)

This package comes with the following components:

CacheAuthenticatorRepository

An implementation of the AuthenticatorRepository which uses a cache to store the authenticator artifacts. Please follow the cache documentation for further information.

DelegableAuthInfoRepository

An implementation of the AuthInfoRepository which delegates the storage of an AuthInfo instance to its appropriate DAO.

📘

Info

Due the nature of the different auth information it is hard to persist the data in a single data structure, expect the data gets stored in a serialized format. With this implementation it is possible to store the different auth info in different backing stores. If we speak of a relational database, then the auth info can be stored in different tables. And the tables represents the internal data structure of each auth info object.

For testing or development purpose, the package comes with an in-memory DAO implementation, which can be used with the DelegableAuthInfoRepository. The following example shows how the DAO can be used in conjunction with the repository.

val passwordInfo = new PasswordInfo(...)
val oAuth1Info = new OAuth1Info(...)
val oAuth2Info = new OAuth2Info(...)
val openIDInfo = new OpenIDInfo(...)

val passwordInfoDAO = new InMemoryAuthInfoDAO[PasswordInfo]
val oAuth1InfoDAO = new InMemoryAuthInfoDAO[OAuth1Info]
val oAuth2InfoDAO = new InMemoryAuthInfoDAO[OAuth2Info]
val openIDInfoDAO = new InMemoryAuthInfoDAO[OpenIDInfo]

val repository = new DelegableAuthInfoRepository(
  passwordInfoDAO, 
  oAuth1InfoDAO, 
  oAuth2InfoDAO, 
  openIDInfoDAO
)

repository.save(LoginInfo(...), passwordInfo)
repository.save(LoginInfo(...), oAuth1Info)
repository.save(LoginInfo(...), oAuth2Info)
repository.save(LoginInfo(...), openIDInfo)

🚧

Warning

The DAO is a not thread-safe implementation which should only be used for testing or development purpose. Your dependency injection framework should also instantiate the DAO as singleton so that the stored data is always available.

Alternative DAO implementations

Following you can find a list of alternative DAO implementations for different storage systems.

Reactive Mongo

An implementation which uses the ReactiveMongo driver to persist the AuthInfo in a MongoDB database. The documentation can be found in the GitHub repository.


What’s Next