Search code examples
playframework-2.0playframework-2.1securesocial

Secure Social Play 2.x / Keeping transient, unregistered (i.e. hasn't logged in yet) user in session?


I'm using Play Framework (Scala) and SecureSocial to authenticate users.

I'd like my site to allow non-logged-in users to browse and populate certain user data (e.g. email address) during their session.

I have a custom UserService implementation that persists logged in users to the DB (and my own User model object that implements the securesocial.core.Identity Trait)

I have an UnregisteredUser subclass which I'd like to persist in the session while the user isn't logged in.

What's the best practise for this, please?


Solution

  • I imagine your particular goal is to efficiently gather the whole data relative to an unregistered user in each of your interested controller's action. Problem: We want to retrieve a build UnregisteredUser directly gathering all these data in session.

    First, persist the UnregisteredUser data whenever you want in the session using the basic Play 2's api Sessions:

    Ok("Welcome Guest User!").withSession(
      "email" -> "unregisteredUserEmail"  //  assuming a randomly chosen Id since not logged yet
      // many other data here
    )
    

    Then, you may simply write a trait extending your actual SecureSocial trait containing:

    case class GuestRequest[A](optUnregisteredUser: Option[UnregisteredUser], request: Request[A]) extends WrappedRequest(request)
    
    def GuestAction(f: GuestRequest[AnyContent] => Result): Action[AnyContent] = {
        implicit request => {
          val optUnregisteredUserEmail = session.get("currentUnregisteredUser")
          val unregisteredUser = UnregisteredUser(optUnregisteredUserEmail) 
          f(GuestRequest(optUnregisteredUser, request))
    }
    

    In each of your concerned controller, you would simply do:

    def addToCard = GuestAction {
        implicit request =>
          val currentUnregisteredUser: UnregisteredUser = optUnregisteredUser.getOrElse(.....)
          //remaining instructions here
      }
    

    --------------UPDATE-------------

    Indeed, you could use UserAwareAction from existing SecureSocial trait for both User styles. Thus, you have to override UserAwareAction in your trait extending SecureSocial, in order to combine features:

        override def UserAwareAction[A](p: BodyParser[A])(f: RequestWithUser[A] => Result) = Action(p) {
            implicit request => {
              val user = for (
                authenticator <- authenticatorFromRequest;
                user <- userServices.findByUserName(authenticator.userName)
              ) yield {
                touch(authenticator)
                user
              }
              if(user.isEmpty){  //meaning user is not logged
                f(RequestWithUser(tryToBuildUnRegisteredUser, request))  //setting your unregisteredUser
              }
              else{
                f(RequestWithUser(user, request))
              }
            }
          }
    
     private def tryToBuildUnregisteredUser = {
        val optUnregisteredUserEmail = session.get("currentUnregisteredUser")
        optUnregisteredUserEmail match {
          case Some(e) => Some(UnregisteredUser(e)) 
          case _ => None
        }
     }
    

    Of course, you are free to refactor it :)