Search code examples
databaseplayframeworksecuresocial

How to persist user registrations using Play 2.0 and SecureSocial


I am using Play 2.0.4 with SecureSocial. I would like to use a UsernamePasswordProvider to persist user registrations to a database.

I am relatively new to Java and the Play Framework so I'm not really sure how to get this done. I got as far as creating a simple model called 'User' but don't know how to go further from here.

I hope that there's somebody out there who might be able to help me and I'm sure this has been done before. Thanks very much in advance!

@Entity
public class User extends Model {
  //
}

Solution

  • assuming you have Play2.0 configured for using SecureSocial, you can start from this question: Play SecureSocial Persistance with Java I've managed to make that work with some minor corrections:

    You need some local representation of your users to take care of storing user info to your persistence layer. I used Ebean and a simple LocalBean. Something like this will do it:

    package models;
    
    import java.util.List;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import play.db.ebean.Model;
    import play.data.validation.*;
    import play.data.format.*;
    
    @Entity
    public class LocalUser extends Model {
    
        @Id
        public String id;
        public String name;
        public String email;
        public String password;
        public String provider;
            public String firstName;
            public String lastName;
    
            public static Finder<String, LocalUser> find = new Finder<String, LocalUser (String.class,LocalUser.class);
    
    
    /**
     * Retrieve a User using an email.
     */
        public static LocalUser findByEmail(String email) {
            return find.where().eq("email", email).findUnique();
        }
    }
    

    For my Token class I used the same as the one proposed in the mentioned question:

    package models;
    
    import java.util.Date;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import play.db.ebean.Model;
    
    @Entity
    public class LocalToken extends Model {
    
        private static final long serialVersionUID = 1L;
    
        @Id
        public String uuid;
        public String email;
        public Date createdAt;
        public Date expireAt;
        public boolean isSignUp;
        public static Finder<String, LocalToken> find = new Finder<String, LocalToken>(
            String.class, LocalToken.class
        );
    }
    

    For the UserService, I had to make some minor corrections to the mentioned question, to make it work on the newer version of SecureSocial, in which the user().id property was replaced by identityId():

    package services;
    
    
    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.List;
    
    import org.joda.time.DateTime;
    
    import models.LocalToken;
    import models.LocalUser;
    import play.Application;
    import play.Logger;
    import scala.Option;
    import scala.Some;
    import securesocial.core.AuthenticationMethod;
    import securesocial.core.Identity;
    import securesocial.core.PasswordInfo;
    import securesocial.core.SocialUser;
    import securesocial.core.IdentityId;
    import securesocial.core.java.BaseUserService;
    import securesocial.core.java.Token;
    
    public class SqlUserService extends BaseUserService {
    
        public SqlUserService(Application application) {
            super(application);
        }
    
        @Override
        public void doDeleteExpiredTokens() {
            if (Logger.isDebugEnabled()) {
                Logger.debug("deleteExpiredTokens...");
            }
            List<LocalToken> list = LocalToken.find.where().lt("expireAt", new DateTime().toString()).findList();
            for(LocalToken localToken : list) {
                localToken.delete();
            }
        }
    
        @Override
        public void doDeleteToken(String uuid) {
            if (Logger.isDebugEnabled()) {
                Logger.debug("deleteToken...");
                Logger.debug(String.format("uuid = %s", uuid));
            }
            LocalToken localToken = LocalToken.find.byId(uuid);
            if(localToken != null) {
                localToken.delete();
            }
        }
    
        @Override
        //public Identity doFind(UserId userId) {
        public Identity doFind(IdentityId identityId){
            if (Logger.isDebugEnabled()) {
                Logger.debug(String.format("finding by Id = %s", identityId.userId()));
    
            }
            LocalUser localUser = LocalUser.findByEmail(identityId.userId());
            if(localUser == null) return null;
            SocialUser socialUser = new SocialUser(new IdentityId(localUser.id, localUser.provider),    
                localUser.firstName, 
                localUser.lastName, 
                String.format("%s %s", localUser.firstName, localUser.lastName),
                Option.apply(localUser.email), 
                null, 
                new AuthenticationMethod("userPassword"),
                null, 
                null, 
                Some.apply(new PasswordInfo("bcrypt", localUser.password, null))
            );  
            if (Logger.isDebugEnabled()) {
                Logger.debug(String.format("socialUser = %s", socialUser));
            }
            return socialUser;
        }
    
    
        @Override
        public Identity doFindByEmailAndProvider(String email, String providerId) {
            List<LocalUser> list = LocalUser.find.where().eq("email", email).eq("provider", providerId).findList();
            if(list.size() != 1){
                Logger.debug("found a null in findByEmailAndProvider...");
                return null;
            }
            LocalUser localUser = list.get(0);
            SocialUser socialUser = 
                    new SocialUser(new IdentityId(localUser.email, localUser.provider),
                            localUser.firstName, 
                            localUser.lastName, 
                            String.format("%s %s", localUser.firstName, localUser.lastName),
                            Option.apply(localUser.email), 
                            null, 
                            new AuthenticationMethod("userPassword"),
                            null, 
                            null, 
                            Some.apply(new PasswordInfo("bcrypt", localUser.password, null))
                       );  
            return socialUser;
        }
    
        @Override
        public Token doFindToken(String token) {
            if (Logger.isDebugEnabled()) {
                Logger.debug("findToken...");
                Logger.debug(String.format("token = %s", token));
            }
            LocalToken localToken = LocalToken.find.byId(token);
            if(localToken == null) return null;
            Token result = new Token();
            result.uuid = localToken.uuid;
            result.creationTime = new DateTime(localToken.createdAt);
            result.email = localToken.email;
            result.expirationTime = new DateTime(localToken.expireAt);
            result.isSignUp = localToken.isSignUp;
            if (Logger.isDebugEnabled()) {
                Logger.debug(String.format("foundToken = %s", result));
            }
            return result;
        }
    
        @Override
        public Identity doSave(Identity user) {
            if (Logger.isDebugEnabled()) {
                Logger.debug("save...");
                Logger.debug(String.format("user = %s", user));
            }
            LocalUser localUser = null;
            //localUser = LocalUser.find.byId(user.id().id());
            localUser = LocalUser.find.byId(user.identityId().userId());
    
            if (localUser == null) {
                Logger.debug("adding new...");
                localUser = new LocalUser();
                //here was localUser.id = user.id().id();
                localUser.id = user.identityId().userId();
                localUser.provider = user.identityId().providerId();
                localUser.firstName = user.firstName();
                localUser.lastName = user.lastName();
                localUser.email = user.email().get();
                localUser.password = user.passwordInfo().get().password();
                localUser.save();
            } else {
                Logger.debug("existing one...");
                localUser.id = user.identityId().userId();
                localUser.provider = user.identityId().providerId();
                localUser.firstName = user.firstName();
                localUser.lastName = user.lastName();
                localUser.email = user.email().get();
                localUser.password = user.passwordInfo().get().password();
                localUser.update();
            }
            return user;
        }
    
        @Override
        public void doSave(Token token) {
            LocalToken localToken = new LocalToken();
            localToken.uuid = token.uuid;
            localToken.email = token.email;
            try {
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                localToken.createdAt = df.parse(token.creationTime.toString("yyyy-MM-dd HH:mm:ss"));
                localToken.expireAt = df.parse(token.expirationTime.toString("yyyy-MM-dd HH:mm:ss"));
            } catch (ParseException e) {
                Logger.error("SqlUserService.doSave(): ", e);
            }
            localToken.isSignUp = token.isSignUp;
            localToken.save();
        }
    }
    

    That's it. Please note that passwords are stored hashed using Bcrypt, so you need to hash you passwords for it to work. The easiest way to get your hashed pass is simple to use the registration process provided by SecureSocial.

    Hope this helps.

    L.