Integrating GaitAuth™ into Your Android App

GaitAuth™ is an SDK offered by UnifyID that allows you to authenticate your users based on how they move. This post demonstrates the process of adding GaitAuth to a simple Android app.

The UnifyID Developer Portal has high-level documentation that covers the basics of the GaitAuth SDK integration process. There are also auto-generated JavaDocs available for the specific details. The goal of this post is to meet the two in the middle and give some more color to the integration process. By the end of the post we’ll have built a sample Android app that uses GaitAuth to train and test a GaitModel. Along the way, we’ll also explore relevant best practices and important implementation details.

The Sample App

All of the sample app code is available on GitHub. To get a feel for the training and testing process you can build the app on your phone and try it for yourself. If you want to get right to building your own app, you can treat the repository as a collection of helpful code snippets. You can also use the code to follow along with this post in depth. Not all of the code is shown in this post, the code that is shown has been abbreviated for simplicity’s sake. Links to the original code on GitHub are provided at the top of the snippets.

The sample app closely mirrors the functionality of the GaitAuth SDK. On the starting screen you are presented with the option to create or load a model. You’ll want to create a new model when you first use the app. In future uses, you can use the load option to skip training. After creating a model you’re ready to collect data and train the model. First, turn on collection (it will continue to collect even if the phone is locked or the app is backgrounded or closed). Once you’ve collected some features you can add them to the model. After you’ve collected enough data you can train the model. While you wait for the model to train (which may take a few hours), you can manually refresh its status.

If the model fails to train you’ll need to start over by pressing the trashcan icon in the top left corner. When the model successfully trains you’re ready to test it. Turn on feature collection and walk around for a while. When you are ready, stop collecting features and score them. This will graph the scores and show you an authentication decision.

Now that we know what the sample app does, let’s go build it.

Configuration & Initialization

Adding GaitAuth to our Gradle configuration is a good starting point. We’ll also need to request some permissions in the Android Manifest. We can follow the steps outlined in the Developer Portal documentation to take care of that.

Before the GaitAuth SDK can be used anywhere in the app it must be initialized. The initialization is trivial; always initializing it before using it is harder. In something straightforward like this sample app we can simply initialize it in the onCreate method of the MainActivity.

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L144
if (GaitAuth.getInstance() == null) {
    // First initialize the UnifyID Core SDK
    UnifyID.initialize(getApplicationContext, SDK_KEY, USER, new CompletionHandler() {
        @Override
        public void onCompletion(UnifyIDConfig config) {
            // Second initialize the GaitAuth SDK
            GaitAuth.initialize(context, config);
            // Save these values for debugging purposes
            Preferences.put(Preferences.CLIENT_ID, config.getClientId());
            Preferences.put(Preferences.INSTALL_ID, config.getInstallId());
            Preferences.put(Preferences.CUSTOMER_ID, config.getCustomerId());
            startGaitAuthService(context);
            route();
        }
    });
}

When initialization succeeds we squirrel away a couple of relevant details in the Shared Preferences (the Preferences class hides the idiosyncrasies of the Shared Preferences API). These will come in handy for debugging. You can view them by tapping on the info icon in the top right corner of the sample app. We also start a foreground service — more on this later. Finally, we call route which determines what the app does next.

In an app with a more complex workflow, initialization of the GaitAuth SDK may not be so simple. In these scenarios we recommend you wrap the SDK in a class that protects its usage. With some simple synchronization this wrapper can ensure that the SDK is never used uninitialized.

Two important points remain. First, remember that the SDK key is a sensitive value and should be protected. In the sample app we ask the user to enter the SDK key the first time they use the app. For an app in production, something like Android Keystore can be used. Second, the user value has a few important stipulations on it. It must be unique, immutable, and should be unrelated to any personally identifiable information. We don’t recommend using things like email addresses or phone numbers for the user value.

The GaitModel

Now that we’ve gotten through the drudgery of initialization it’s time to talk about the star of the show — the GaitModel. In a sense the sample app can be viewed as a tool for managing the lifecycle of a GaitModel. It creates a new model, loads training data into it, initiates training, and then tests the model. For this reason, the sample app’s routing is based on the status of the current GaitModel.

The sample app uses a single-activity/multiple-fragment architecture where each fragment is a different screen. In the MainActivity after the initialization of the GaitAuth SDK the following routing code is run.

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L185
private void route() {
    // Load the id of the current GaitModel
    String modelId = Preferences.getString(Preferences.MODEL_ID);
    if (Strings.isNullOrEmpty(modelId)) {
        showFragment(new SelectModelFragment());
    } else {
        loadModel(modelId);
    }
}

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L287
private void loadModel(String modelId) {
    AsyncTask.execute(() -> {
        model = GaitAuth.getInstance().loadModel(modelId);
        Preferences.put(Preferences.MODEL_ID, modelId);
        renderFragmentBasedOnModelStatus(model.getStatus());
    });
}

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L255
private void renderFragmentBasedOnModelStatus(GaitModel.Status status) {
    switch (status) {
        case CREATED:
            showFragment(FeatureCollectionFragment.build(gaitAuthService.isTraining()));
            break;
        case TRAINING:
            showFragment(new ModelPendingFragment());
            break;
        case FAILED:
            showFragment(ModelErrorFragment.newInstance(model.getReason()));
            break;
        case READY:
            showFragment(TestingFragment.build(gaitAuthService.isTesting()));
            break;
        default:
            // treat it as a failure
            showFragment(ModelErrorFragment.newInstance("unknown model status"));
            break;
    }
}

First the method route looks for the id of the current GaitModel and asynchronously loads it with the help of loadModel. After the model is loaded, renderFragmentBasedOnModelStatus is called. A simple switch statement then sends the user to the screen matching the current state of the model.

The method route will not find a model id on a user’s first time through the app or if the reset button was pressed. In these cases the user is immediately sent to the SelectModelFragment. From here, when the user clicks on the “Create Model” button, onCreateModelPressed is executed. It builds a new GaitModel, saves the model id in the shared preferences, and sends the user to the FeatureCollectionFragment. Alternatively, the user can opt to load a pre-existing model which leverages the same loadModel method.

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L315
public void onCreateModelPressed() {
    AsyncTask.execute(() -> {
        model = GaitAuth.getInstance().createModel();
        Preferences.put(Preferences.MODEL_ID, model.getId());
        showFragment(new FeatureCollectionFragment());
    });
}

Feature Collection

So we have a GaitModel locked and loaded, but now what? The model is useless without training data so let’s start there. A GaitModel is trained on a collection of GaitFeatures which are data points collected from the user’s movement. These GaitFeatures are given to the model via the add method which has the signature void GaitModel.add(Collection<GaitFeature> features). Later during training, only the features explicitly added to the model will be used.

Now that we know how to use the features, how do we actually collect them? This is achieved by registering a FeatureEventListener that will fire the onNewFeature callback for every feature collected. Correctly managing feature collection requires tackling two key issues: feature storage and backgrounding.

Storing Features

In a naive implementation of feature collection, every time a new feature was received it would be immediately added to the GaitModel. There are two problems with this. First, adding features to the GaitModel may use network resources or trigger other expensive operations and thus is inefficient to call frequently. Second, if the model fails when training you will need to recollect all new data for the new model since you didn’t persist it anywhere.

The sample app solves the feature storage problem with a thread-safe FeatureStore class. This class exposes a method with the signature void add(GaitFeature feature), which serializes the feature and appends it to a file on disk. At a future time (when the user clicks the “Add Feature” button) all of the features can be loaded from disk, deserialized and returned via the method getAll with the signature List<List<GaitFeature>> getAll(). Note that it returns the features partitioned into multiple lists. This is because adding thousands of features to a model at once should be avoided. Finally, there is an empty method to clear the file after adding features. This prevents adding features twice. Deleting the features after using them reintroduces the persistence problem though. The sample app does it anyways to avoid re-adding features to the model in a simple manner.

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/GaitAuthService.java#L163
public void onNewFeature(GaitFeature feature) {
    featureStore.add(feature);
    int count = Preferences.getInt(Preferences.FEATURE_COLLECTED_COUNT) + 1;
    Preferences.put(Preferences.FEATURE_COLLECTED_COUNT, count);
}

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L363
public int onAddFeaturesPressed() {
    FeatureStore featureStore = FeatureStore.getInstance(this);
    List<List<GaitFeature>> chunks = featureStore.getAll();
    int uploadedCounter = 0;

    for (List<GaitFeature> chunk: chunks) {
        model.add(chunk);
        uploadedCounter += chunk.size();
    }
    if (uploadedCounter > 0) { // only truncate file after we uploaded something
        featureStore.empty();
    }
    return uploadedCounter;
}

In a production application there are many improvements you would want to make to FeatureStore. First and foremost, it should implement some form of in-memory buffering. Writing to disk for every feature you collect is slow and can lead to poor battery-life. Second, it should rotate files to avoid size limitations and corruption. Third, it should not clear the file after adding the features to the model. Rather, it should keep track of what features have been added and only add the new ones.

Backgrounding

The second key issue to solve for feature collection is backgrounding. It would not be wise to register the FeatureEventListener on the app’s main thread. As soon as the user closed the app, GaitFeatures would no longer be collected. Android services can help us solve this problem. Services provide a way to execute a long-running operation in the background. There are two relevant types of Android services: background and foreground. A background service needn’t provide any indication to the user that it is running, but is more likely to be shut down by the OS if resources are scarce. A foreground service must present a notification to the user indicating its presence the entire time it is alive. But, it is unlikely to be killed by the OS.

The sample app takes the most straightforward approach. In the onCreate method of the MainActivity a foreground service is created regardless of whether or not the service will be used. This avoids complex state management and synchronization issues that arise when dynamically building a service. In the onCreate method the activity binds to the service. And in the onStop method it unbinds from the service. This gives the activity direct access to the feature collection service.

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L104
protected void onStop() {
    super.onStop();
    if (gaitAuthServiceBound.get()) {
        unbindService(gaitAuthServiceConnection);
        gaitAuthServiceBound.set(false);
    }
}

You may want to handle services differently in a production app. For example, you may only want to start the service when you are actually collecting features. If you go this route make sure to take special care in synchronizing the usage of the GaitAuth SDK.

Final Considerations

That was a lot of details. Let’s take a step back for a moment and consider the entire training process. Feature collection for training is the most critical stage for the GaitAuth SDK. To get good authentication results you need to have a well trained model. The Developer Portal documentation has a number of suggestions on how to best do this.

Training the Model

The hard work of feature collection was well worth the effort. We now have a plethora of GaitFeatures to train with. We’re nearly ready to use our GaitModel, but first we need to train it. Thankfully, training is easy. When a user clicks on the “Train Model” button the method below is called. In a background thread it kicks off the training process for the model and sends the user to the ModelPendingFragment.

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L397
public void onTrainModelPressed() {
    AsyncTask.execute(() -> {
        try {
            model.train();
            showFragment(new ModelPendingFragment());
        } catch (GaitModelException e) {
            showToast("Failed to start training model.");
        }
    });
}

At the pending model screen a user can manually refresh the status of a model while they wait for it to train.

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L436
public void onRefreshPressed() {
    AsyncTask.execute(() -> {
        try {
            model.refresh();
            renderFragmentBasedOnModelStatus(model.getStatus());
        } catch (GaitModelException e) {
            showToast("Failed to refresh model status.");
        }
    });
}

After refreshing the status of the model, our old friend renderFragmentBasedOnModelStatus is called to bring the user to the right screen given the model’s status. If the training failed for some reason the ModelErrorFragment will load. From there the user has no choice other than to reset and start over. After a successful training, the user will be presented with the TestingFragment. And of course, if the model status is still TRAINING after a refresh then no navigation will occur.

Testing the Model

The hard work is over now — we’ve trained a GaitModel and can now reap the benefits. All of the testing functionality is managed by the Authenticator interface. When you instantiate an Authenticator you pass it a GaitModel and a scoring policy. Once created, it automatically starts collecting features. You can ask it for an authentication decision at any time and it will say if the user is authenticated or unauthenticated. In the scenario where this is not enough data to make a decision it will return inconclusive. When the user presses the “Start Collection” button onStartCollectionBtnPressed is called. It in turn tells the foreground service to create a new Authenticator object.

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L338
public void onStartCollectionBtnPressed() {
    try {
        gaitAuthService.startFeatureCollectionForTesting(model);
    } catch (GaitAuthException e) {
        showToast("Failed to start feature collection for testing.");
    }
}

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/GaitAuthService.java#L213
public void startFeatureCollectionForTesting(GaitModel model) throws GaitAuthException {
    GaitQuantileConfig config = new GaitQuantileConfig(QUANTILE_THRESHOLD);
    authenticator = GaitAuth.getInstance().createAuthenticator(config, model);
}

A quick aside on the GaitQuantileConfig policy and how it works. In short it will authenticate the user if at least X percent of scores in the session scored Y or more (learn more about scores here). X is known as the quantile and Y is the score threshold. GaitQuantileConfig sets the quantile to 50% by default and in the sample app the score threshold is set to 0.8. The table below shows three example sessions with these numbers. The first session has 60% of scores at or above the 0.8 score threshold, and therefore has an authenticated result. However, the second and third sessions only have 40% and 20% of scores that meet the 0.8 score threshold, and therefore they produce an unauthenticated result.

You can customize more than just the quantile and score threshold of the GaitQuantileConfig. The method setMinNumScores lets you configure how many scores are required for an authentication decision to be made. Any attempt to authenticate with a number of scores less than this minimum will return inconclusive. Similarly, setMaxNumScores configures the maximum amount of scores that will be considered. If there are more scores than the maximum, the most recent scores will be chosen. Finally, setMaxScoreAge determines how old of scores can be used. Scores older than the given age will not be used for an authentication decision.

Back to the action. The sample app requires the user to stop collecting features before they can score them. Note that this is not a requirement of the GaitAuth SDK and is only done to simplify state management. The Authenticator has a stop method which helps us do this. Clicking the “Stop Collection” button kicks off this sequence of events.

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L349
public void onStopCollectionBtnPressed() {
    gaitAuthService.stopFeatureCollectionForTesting();
}

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/GaitAuthService.java#L223
public void stopFeatureCollectionForTesting() {
    if (authenticator != null) {
        authenticator.stop();
    }
}

Now the user can press the “Score Features” button. This entails getting the authenticator from the foreground service and then getting an authentication status from the authenticator. Then it sends the user to the ScoreFragment. The authenticator also returns the individual scores that lead to the authentication decision. ScoreFragment puts these in a graph to help visualize the decision. How you use the authenticator will be highly dependent on the specifics of your use case.

// https://github.com/UnifyID/gaitauth-sample-app-android/blob/470932205438dd2ce43dc6bad586df90c72cc800/app/src/main/java/id/unify/gaitauth_sample_app/MainActivity.java#L469
public void onScoreFeaturesPressed() {
    Authenticator authenticator = gaitAuthService.getAuthenticator();
    
    authenticator.getStatus(new AuthenticationListener() {
        @Override
        public void onComplete(AuthenticationResult authenticationResult) {
            showFragment(scoreFragment);
        }

        @Override
        public void onFailure(GaitAuthException e) {
            showToast("Failed to get scores.");
        }
    });
}

Conclusion

And just like that we’ve integrated GaitAuth into a simple Android application. For more implementation details you can explore the code in depth on GitHub. If you build something with GaitAuth, let us know on social media. We’d love to hear about it. Finally, reach out to us if you have any trouble integrating.

The Power of Trusted Device Registration

Welcome once again to the Power of PushAuth™ blog series! This post builds on the previous posts by providing an extension of our sample project with feature and security enhancements.

The Problem of Unchecked Device Registration

In the “Is push authentication perfect?” section of the first post in the series, we pointed out some shortcomings of relying on push authentication as a means to authenticate users. Recall the following from that section:

“There’s also the issue of trusting a device to be associated with its true user during registration, as well as not allowing attackers to register devices under the true user’s account.”

The first iteration of the open source sample project did not provide a solution to this problem. With that initial, simple implementation of PushAuth, there was virtually no confidence in the devices registered under a given username. If an attacker were able to obtain the SDK key and one or more usernames registered on the server, they could configure the app on their phone and fraudulently accept or reject push notification login attempts. This security hole allows attackers to impersonate true users and access their resources in the website, or to lock a true user out of accessing their own resources. Not great, considering this is supposed to be a method of increasing the security of the authentication flow.

Trusted Device Registration

The extension presented in this post, however, does include trusted device registration! It also provides the added feature of user registration in the website. Remember that the initial project required users to be created in the console. Only users whose username and password were inserted to the database in that manner were able to log in to the website and receive push notifications. Now users register in the website directly. Once a user signs up in the website, they configure the sample app on their phone in order to authorize future login attempts. This is where trusted device registration comes into play.

What’s different for the user?

After dictating what username and password to associate with a new user, the website displays a four-digit pairing code. The user is instructed to enter that pairing code in the sample app with their chosen username. If the values match, then the user will receive push notifications during login attempts and be able to respond accordingly. If the username and pairing code do not match, then registration of that mobile device fails. This means they will not be able to receive push notifications to confirm or deny login attempts. An attacker will not know that unique pairing code, so they are unable to impersonate a user in the app.

How does this work?

The PushAuth sample server now has a verification endpoint, /users/trust, which accepts webhook requests from UnifyID’s PushAuth service. UnifyID’s server makes an HTTP POST request to a configured target URL, which is set in the project’s dashboard. The request body contains the username and the pairing code. The /users/trust endpoint looks up the given username and determines if the provided pairing code matches the server-generated code displayed during that user’s registration. If the request returns 200, then the user is considered verified and the device trusted. However, if the request returns anything else, the user is not considered verified and device registration fails.

The webhook target URL must use the https scheme and requests use the ‘Basic’ HTTP Authentication scheme, which can be used to validate requests. Further, the four-digit alphanumeric code generated by the server is single-use. These details collectively make it harder for an attacker to impersonate users. An attacker does not have access to the pairing code during initial sign up and they also cannot reuse the pairing code, thus ensuring that devices registered with users are trusted to belong to the true user.

Note: The way we chose to implement trusted device registration with a pairing code is by no means the only method of implementing trusted registration via webhook.

What are the limitations?

If you remember from our first post in the series, there will seemingly always be security holes in a login flow. This enhancement is no exception. For one, the pairing code is only displayed during user registration. What if the user accidentally closes out the tab and then has no way to pair their device? Or what if something happens with the device or app on their device and they have no way to reconfigure an app to be tied to their user? There’s also the concern of in-person over-the-shoulder access to the pairing code, where an attacker could see the four digits and enter the code on their phone before the true user is able to do so. However, just like before, there are solutions to those problems as well; they just aren’t included here. The extension we provide is certainly a big security improvement and patches one of the larger holes that existed in the initial sample project.

Developer Changes

Now that we’ve gone over the concept of trusted device registration and the value it adds to the PushAuth sample project, we’ll walk you through the actual changes from a developer’s perspective. The new repositories can be found here:

We have videos on the UnifyID YouTube channel to walk you through the full end-to-end flow of adding PushAuth to a website login flow with trusted registration.

Trusted Registration Webhook

First and foremost, you’ll need to enable trusted registration for your project in the dashboard. Reference the Setting up Trusted Registration section of our documentation to do so.

The server needs to be publicly available for the target URL to be used. If you want to follow along the tutorial without hosting the web server, we suggest using ngrok to expose the sample rails server running locally on your machine to the internet. The steps shown in this post will do that.

$ ngrok http 3000

Session Status                online
Account                       morganfrisby (Plan: Free)
Version                       2.3.35
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://9c6b07ea6861.ngrok.io -> http://localhost:3000
Forwarding                    https://9c6b07ea6861.ngrok.io -> http://localhost:3000

The target URL is the https URL forwarding to your local server, with /users/trust appended. For example:

Once you add the target URL, trusted registration is enabled for your project. The username and password displayed are used in HTTP Basic Authentication. The username value is set to unifyid, and the password is an auto-generated random string. This value can be rotated, should it become compromised.

Now that the trusted registration webhook is set up in the dashboard, you’re ready to move on to setting up the server.

Server Setup

Other than that, server setup more or less follows the instructions from the first tutorial. Since users are now registered on the website, there is no need to create users in the console during server setup. The other change is the addition of basic_username and basic_password to the credentials file, which are the values found in your project dashboard under the Trusted Registration Webhook section, shown above.

The streamlined steps to spin up the server are now:

$ git clone https://github.com/UnifyID/pushauth-sample-server-reg.git
$ cd pushauth-sample-server-reg
$ bundle install
$ yarn install --check-files
$ bundle exec rails db:migrate

$ EDITOR=vim rails credentials:edit
unifyid:
  server_api_key: <your_key_goes_here>
  basic_username: <basic auth username for /users/trust webhook endpoint>
  basic_password: <basic auth password for /users/trust webhook endpoint>

$ bundle exec rails server

Feel free to reference the detailed tutorial in our previous post for more context around those commands, or the technical details blog post to understand how the server was built. Our next post in the series will provide the technical details of the trusted registration extension of the sample server.

You can see the new website screenshots here (notice the pairing code in the third screenshot):

Sample App Setup

Nothing changes from the developer’s perspective for the iOS and Android sample apps; simply clone the updated repositories and follow the same instructions from earlier in the blog series. Those two posts can be found here:

The iOS sample app screens will look like:

The Android sample app screens now look like:

That’s it! Thanks for following along with the Power of PushAuth™ blog series. The next, and final, post will include the technical details of adding trusted registration to the sample project. Until then, feel free to reach out to us with questions or comments at https://unify.id/contact-us/.

How to Implement PushAuth™: Android Mobile App

This post is part of the Power of PushAuth™ blog series. The first post of the series was a comprehensive guide to push authentication. The next three posts of the series comprise an end-to-end sample implementation of PushAuth in a simple user login flow. The tutorial breakdown is as follows:

  1. Web Server tutorial
  2. iOS Mobile App tutorial
  3. Android Mobile App tutorial (this post)

The tutorial in this post builds on the web server from the first tutorial. With your web server set up and running, you now need a mobile app to receive and respond to notifications. This post will help you build the Android mobile app to do so; then you will be able to leverage the power of PushAuth for login requests!

Setup

To follow this tutorial, you will need:

  • An Android device running Android 7.0 or higher
  • A computer with Android Studio 4.0 installed
  • JDK 8 installed (we recommend using jabba)

Step 1 : Cloning the Project

The sample Android mobile app code for this project is provided in the pushauth-sample-app-android GitHub repository. Clone this repo to your local machine:

$ git clone https://github.com/UnifyID/pushauth-sample-app-android.git

Open Android Studio, select “Open an existing Android Studio project”, and select the directory of the cloned repository. It should look something like this:

Step 2: Set up Firebase Cloud Messaging (FCM)

Firebase Cloud Messaging (FCM) is the platform used to send notifications to Android devices. You will first need to set up Firebase in your app by following the instructions at https://firebase.google.com/docs/android/setup. After doing this, ensure that a google-services.json file is present under the app/ directory of the project in Android Studio.

Next, navigate to the Firebase project settings of your app in your browser and select the “Cloud Messaging” tab. The token labeled “Server key” is what you will need to provide to UnifyID in the next section, so copy that value.

Step 3: Providing Push Credentials to UnifyID

Now you have the Firebase Cloud Messaging (FCM) server key copied, you will provide it to UnifyID so that PushAuth can send push notifications to the sample app on your phone. You’ll provide this value in your project’s dashboard. Follow the instructions in the Developer Portal docs to do so. After you’ve done this, your project dashboard will indicate they are successfully uploaded:

Step 4: Building the Project

Now, back to Android Studio. Make sure that your Android device is plugged in to your computer, has developer tools and USB debugging enabled, and is available in the top center of Android Studio. Also make sure that the google-services.json file is located under the app/ directory of the project.

With everything set up, click the green triangle or press Control+R to run the app. The following screen should appear on your device:

Step 5: Mobile App Settings

You now have all the values necessary for configuration! Tap the gear icon in the top right corner of the sample app’s Configuration screen. For SDK key, enter your UnifyID project’s SDK key value from the Dashboard. The User string should be the same value that you used when creating a user in the web server tutorial, e.g. “Morgan”. If these values do not match, you will not be able to successfully respond to push notifications in the login flow.

After setting those values and clicking “Confirm”, the app is ready to receive your PushAuth login requests! The app will remain on the “Waiting for PushAuth requests” page until it receives a PushAuth authentication request.

Now you can go through the full login flow by entering your username and password on the login page, respond to the notification received by this app on your phone, and be successfully logged into the website.

That’s it! Now you have a simple login flow that integrates PushAuth. Stay tuned for the rest of the posts in the series, make sure to share this post and reach out to us if you have any questions, comments or suggestions!