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 needs.

Login information

The LoginInfo case class acts as Silhouette’s identity ID, and it helps identify users in the Silhouette workflow. LoginInfo contains 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 your application supports the concept of “merged identities”, i.e., users are able to authenticate through any of multiple providers, then you must ensure that login information is stored separately for each provider. Later you will see how this can be implemented.

Implement an identity

To define a user for your application, you need 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 (userID), the login information for the provider which authenticates that user (loginInfo, of type LoginInfo), and other information such as name and email.

case class User(
  userID: Long,
  loginInfo: LoginInfo,
  name: String,
  email: Option[String]) extends Identity

📘

Silhouette is flexible

Note that our User case class does not have to include a property of type LoginInfo if it instead provides a way of deriving a LoginInfo instance from the other properties.

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 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)
}

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 a 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.


What’s Next