Written by Adrian Kremski
Android Developer
Published April 13, 2017

Realm Mobile Platform: Offline-first TaskManager app – Authentication

In this part of the series “Realm Mobile Platform: Offline-first TaskManager app”, we are going to implement authentication mechanism in our TaskManager app. The article includes both email and Facebook mechanisms!

If you are new to my series, then here is the list of all articles you can read:

Part 1: REALM MOBILE PLATFORM: OFFLINE-FIRST TASKMANAGER APP – INTRO
Part 2: REALM MOBILE PLATFORM: OFFLINE-FIRST TASKMANAGER APP – BASICS OF REALM
Part 3: REALM MOBILE PLATFORM: ANDROID “TASKMANAGER” – REALM OBJECT SERVER
Part 5: Coming soon

Let’s get going with this part!

1. Email authentication

We will start by enabling sync features of realm in our gradle build.

app/build.gradle

android {
  ...
}

realm {
    syncEnabled = true
}

dependencies {
  ... 
}

As a second step, we need to specify an AUTH_URL constant which will be later used in our authentication logic (<ip_address> is the address of your machine hosted on Amazon).

public class RealmTaskManagerApplication extends Application {

    public static final String OBJECT_SERVER_IP = "<ip_address>";
    public static final String AUTH_URL = "http://" + OBJECT_SERVER_IP + ":9080/auth";

    @Override
    public void onCreate() {
        super.onCreate();
        Realm.init(this);

        RealmConfiguration config = new RealmConfiguration.Builder().build();
        Realm.setDefaultConfiguration(config);
    }

}

Next, we will update the login button callback in our LoginActivity with correct logic.

public class LoginActivity extends AppCompatActivity {

    ...

    @OnClick(R.id.login)
    public void login() {
        final String email = usernameLabel.getText().toString();
        final String password = passwordLabel.getText().toString();

        progressView.setVisibility(View.VISIBLE);

        SyncUser.loginAsync(SyncCredentials.usernamePassword(email, password, false), RealmTaskManagerApplication.AUTH_URL, new SyncUser.Callback() {

            @Override
            public void onSuccess(SyncUser user) {
                showMainScreen();
                Toast.makeText(getBaseContext(), "You are logged in", Toast.LENGTH_SHORT).show();
                progressView.setVisibility(View.GONE);
            }

            @Override
            public void onError(ObjectServerError error) {
                Toast.makeText(getBaseContext(), "Log in error", Toast.LENGTH_SHORT).show();
                progressView.setVisibility(View.GONE);
            }
        });
    }
}

In realm the sign in and sing up operations are both done by invoking SyncUser.loginAsync(...) method. The difference indicating whether we should create a user or not is specified by boolean flag passed to SyncCredentials.usernamePassword(email, password, false) (true – create user, false – skip creation).
Having that in mind, we can now implement the register method by passing true to SyncCredentials.usernamePassword(...) when creating credentials.

public class LoginActivity extends AppCompatActivity {

    ...

    @OnClick(R.id.register)
    public void register() {
        final String email = usernameLabel.getText().toString();
        final String password = passwordLabel.getText().toString();

        progressView.setVisibility(View.VISIBLE);

        SyncUser.loginAsync(SyncCredentials.usernamePassword(email, password, true), RealmTaskManagerApplication.AUTH_URL, new SyncUser.Callback() {

            @Override
            public void onSuccess(SyncUser user) {
                Toast.makeText(getBaseContext(), "Account created", Toast.LENGTH_SHORT).show();
                progressView.setVisibility(View.GONE);
            }

            @Override
            public void onError(ObjectServerError error) {
                Toast.makeText(getBaseContext(), "Registration error", Toast.LENGTH_SHORT).show();
                progressView.setVisibility(View.GONE);
            }
        });
    }
}

Finally, we need to ensure the login screen won’t be presented to the user when he/she is logged in.

public class LoginActivity extends AppCompatActivity {

    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    if (SyncUser.currentUser() != null) {
        showMainScreen();
    }
}

Result

2. Facebook authentication

Nowadays most of the apps provide the possibility to authenticate with Facebook and we are going to do the same in our case. Before you start connecting Facebook auth to realm, follow this tutorial to set up basic Facebook SDK.

The rest of the implementation is pretty simple. Firstly update the login_activity.xml layout by adding facebook button below our login button.

<com.facebook.login.widget.LoginButton
    android:id="@+id/facebook_login"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:drawablePadding="40dp"
    android:paddingBottom="12dp"
    android:paddingLeft="12dp"
    android:paddingRight="0dp"
    android:paddingStart="12dp"
    android:paddingTop="12dp" />

Secondly, we need to initialize Facebook sdk. Preferably within our Application class.

public class RealmTasksApplication extends Application {

    ...
    @Override
    public void onCreate() {
        super.onCreate();

        FacebookSdk.sdkInitialize(this);

    }
}

Then in the LoginActivity we will set up our callbacks.

public class LoginActivity extends AppCompatActivity {

    @Bind(R.id.facebook_login)
    LoginButton loginButton;

    CallbackManager callbackManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        callbackManager = CallbackManager.Factory.create();

        loginButton.registerCallback(callbackManager, new FacebookCallback() {

            @Override
            public void onSuccess(LoginResult loginResult) {
                SyncCredentials credentials = SyncCredentials.facebook(loginResult.getAccessToken().getToken());

                SyncUser.loginAsync(credentials, RealmTaskManagerApplication.AUTH_URL, new SyncUser.Callback() {
                    @Override
                    public void onSuccess(SyncUser user) {
                        showMainScreen();
                        Toast.makeText(getBaseContext(), "You are logged in", Toast.LENGTH_SHORT).show();
                        progressView.setVisibility(View.GONE);
                    }

                    @Override
                    public void onError(ObjectServerError error) {
                        Toast.makeText(getBaseContext(), "Log in error", Toast.LENGTH_SHORT).show();
                        progressView.setVisibility(View.GONE);
                    }
                });
            }

            @Override
            public void onCancel() {}

            @Override
            public void onError(FacebookException exception) {}
        });

The onSuccess and onError methods are the same as in case of basic email login. The difference here is that we are now creating login credentials by using the token acquired during facebook login SyncCredentials.facebook(loginResult.getAccessToken().getToken());

Finally, to make the authentication work we also need to enable facebook auth in the configuration.ymlfile of our realm object server.

Don’t forget to restart your server!

sudo service realm-object-server restart

Thanks for reading! Part 5 is coming soon!

Written by Adrian Kremski
Android Developer
Published April 13, 2017