3

So I have an app which uses Google App Engine and Google Cloud Endpoints as it's backend in Java. I'm currently working on User authentication and here is what I'm trying to do:

When user first opens the app, they'll have option to either "Login through Facebook" or signup using their email address. Then this data would be stored in a user object and after registration would direct them to the app homepage. It will be saved in their preferences so that they don't need to login every time they open the app (if ever).

Now I heard you can use a custom authenticator for Facebook, but there's not much documentation regarding this. How can I get the email registration and Facebook Login options to be implemented with Google Cloud Endpoint's Authenticator? Or should I make a different approach?

Thanks.

user2109766
  • 115
  • 1
  • 7

2 Answers2

2

My approach is using the Facebook login method (Facebook SDK for Android). The Facebook authentication process returns (on success) an object from which I can get the user's email then I save it in my Endpoints class using Datastore API. To check if user already logged in I chose the SharedPreferences approach with GSON library to parse objects into JSON String and save them in the prefs.

Links and my sample codes below :

Regarding the Authenticator I found this SO answer

More info about Facebook login method

Saving custom objects in SharedPreferences

Getting user's email through Facebook auth

 private void onSessionStateChange(Session session, SessionState state, Exception exception) {
        if (state.isOpened()) {
            if (isSessionCalled == false) {
                Log.i(TAG, "Logged in...");
                System.out.println("Token=" + session.getAccessToken());

                new Request(
                        session,
                        "/me",
                        null,
                        HttpMethod.GET,
                        new Request.Callback() {
                            public void onCompleted(Response response) {
                                if (response != null) {
                                    GraphObject object = response.getGraphObject();
                                    String email = (String) object.getProperty("email");
                                    Log.i(TAG, "user email : " + email);
                                    String firstName = (String) object.getProperty("first_name");
                                    String lastName = (String) object.getProperty("last_name");
                                    mUserTask = new UserAsyncTask();
                                    mUserTask.execute(email);
                                }

                            }

                        }
                ).executeAsync();

                isSessionCalled = true;
            }
            else {
                Log.w(TAG, "session called twice");
            }

            }

        else if (state.isClosed()) {
            Log.i(TAG, "Logged out...");
        }
    }

Storing the user in my backend :

@ApiMethod(name = "storeUserModel")
    public UserModel storeUserModel(UserModel userModel) throws UserAlreadyExistsException, UserNotFoundException {
        logger.info("inside storeUser");
        String email = userModel.getEmail();
        UserModel checkUser = getUserModel(email);
        logger.info("after getUserModel with email " + email);

        if (checkUser == null) {
            logger.info("inside checkUser is NULL");
            DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
            Transaction txn = datastoreService.beginTransaction();
            try {
                Entity userEntity = new Entity(UserModel.class.getSimpleName(), email);
                userEntity.setProperty("nickname", userModel.getNickname());

                // TODO save the pheromones with the key of userEntity
                datastoreService.put(userEntity);
                txn.commit();
                storePheromoneList(userModel.getPheromoneList(), userEntity.getKey(), datastoreService);
            } finally {
                if (txn.isActive()) {
                    logger.severe("rolled back with email : " +  email);
                    txn.rollback();
                }
            }
        }
        else {
            throw new UserAlreadyExistsException();
        }
        return userModel;

    }

A class that triggers calls to my backend

public class EndpointsServer implements Server {

    private static final String TAG = "EndpointsServer";

    final UserModelApi userEndpointsApi;

    public EndpointsServer() {
        UserModelApi.Builder builder = new UserModelApi.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null)
                .setRootUrl("http://10.0.2.2:8080/_ah/api/")
                .setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
                    @Override
                    public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
                        abstractGoogleClientRequest.setDisableGZipContent(true);
                    }
                });

        userEndpointsApi = builder.build();

    }

    @Override
    public User getUser(String email)  {
        User  user = null;
        try {
            Log.d(TAG, "in getUser with email " +email);
            // get user from db
            UserModel userModel = userEndpointsApi.getUserModel(email).execute();

            if (userModel != null) {
                Log.d(TAG, "user != null with email " + email);
                user = new User(userModel);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return user;
    } 
}

Storing user on successful login :

String userString = gson.toJson(user, User.class);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(USER_KEY, userString);
editor.commit();

There's more to it like another client side class to build the api call to the backend and lots of other details. I can post it if you want.

Community
  • 1
  • 1
serj
  • 508
  • 4
  • 20
  • Thanks a lot. But my backend uses Objectify and the facebook login will mainly store the person's profile ID (and access token I guess). If instead the user chose "signup through email" then they would need to provide an email address and password. So I see how the storing of the facebook session works, but can the same method be used to store an email + password as well? Also I would really appreciate it if you could post the other stuff. Thanks :) – user2109766 Mar 13 '15 at 21:14
  • For storing email and password you can use Android Studio login activity template. It will give some start regarding to the UI. In case the user chooses to register through your authentication method it will pretty much the same. Check if email is already in the DB (using Objectify), if not, add it. Then store it the shared prefs. I'll add the part with the api call right now. – serj Mar 14 '15 at 11:47
  • I've added all the code regarding to the backend and client – serj Mar 14 '15 at 11:56
1

I can't speak on Java but I started with Python by looking at this repo on Github: https://github.com/loudnate/appengine-endpoints-auth-example

This shows you an example on how to write a custom authenticator with Facebook Login. Writing your own authentication I think you should be able to find some examples. The only thing you need to do after is to use the same User entity.

And I suggest you do some reading on how OAUTH 2.0 works so you don't get too confused on the task you need to do.

Basically:

  • On your client side, whether web or android, get a facebook access token, sends it to your endpoint service. Exchange for a access token of your own. At the same time, create your User object in datastore and associate the access token.
  • Then all your subsequent request should use this access token to get access to your endpoint backend. (Do a user check on your endpoint API method.)
beast
  • 183
  • 7
  • It doesn't make sense how these is no documentation on doing this in Java. I mean, there is a tutorial on how to implement Google Account authentication. But it seems like Google Cloud Endpoints is not the most popular backend due to the lack of tutorials available. – user2109766 Mar 11 '15 at 22:56
  • I doubt it is not the most popular but certainly it's very heavy to setup in JAVA. (Don't mark my word because I never did JAVA.) In python there are plenty examples but true that custom authentication is lacking, I am not sure what is the reason. But once I finished my implementation, I plan to open source it in python. – beast Mar 12 '15 at 07:58