Android local login


Table of Contents:

Setup:

Demo App Analysis:


Prerequisites


Dependencies

  • Android Studio with Android SDK 16 or higher
  • Android SDK build tools 25.0.0
  • Android NDK
  • Also use Android SDK tools to install CMake

Downloading the repo

Then download the Android SDK repo from https://github.com/miracl/incubator-milagro-mfa-sdk-android

Note that the repo includes a submodule. So after downloading run:

git submodule init
git submodule update

You can rename the repo as you wish.


Building the SDK


To build the SDK:

From Android Studio:

Import the project from the Android Studio welcome screen by clicking on Import project (Eclipse ADT, Gradle, etc.):

studio_import_proj

The assembled aars will be located in /mpinsdk/build/outputs/aar

From Command Line:

  1. Navigate to the top level folder of the Android project
  2. Execute ./gradlew build
  3. The assembled aars will be located in /mpinsdk/build/outputs/aar

Create a demo web app to act as a backend service


Now that the project is imported, in order to enable you to test the demo Android app, you should first of all create a demo MFA web app using one of our web SDKs, as explained in the SDK Instructions section of this documentation. The purpose of this is to have a running service which can authenticate all users who are registering with and logging into your Android app (i.e. the web app will not be used for the purpose of in-browser login). Please note that, currently, only the dotnet SDK supports this functionality.

Once you have used the authentication portal to obtain a Client Id and Secret for your demo web app, you will need to configure the demo web app with these values, using the instructions provided on the web SDK page. It will then need to be hosted on an available url which means it is accessible by the Android app. These steps are illustrated on the web SDK instructions page. A further note is that, if you are setting this up on a simple private network and using IIS Express to run the app which has been configured in Visual Studio, it will be necessary to make sure the firewall of the host machine allows incoming connections to the relevant port, and that the .vs/config/applicationhost.config file contains bindings which make it available:

<bindings>
    <binding protocol="http" bindingInformation="*:5000:127.0.0.1" />
    <binding protocol="http" bindingInformation="*:5000:" />
</bindings>

The <binding protocol="http" bindingInformation="*:5000:" /> line will allow binding to any IP. Using, for example, <binding protocol="http" bindingInformation="*:5000:192.168.1.18" /> would allow binding to a specific private IP only.

Note that, for the web app settings in the authentication portal you will need to make sure there is a redirect url which has the private IP of the host machine as its base:

Once the demo web app is running and hosted it will be available for the Android app to connect to as the backend service to which your Android app can authenticate.


Add mpin_cid to the project and build the APK


Before you build the APK for the demo Android app to be installed on an Android device, you will need to obtain your company mpin_cid as the owner of the MFA web app. This is visible as a tooltip in the top right corner of your company dashboard in the portal:

view cid

Once you have the mpin_cid, open inappSample/src/main/res/values/strings.xml from within your project folder, and enter it into the following:

<string name="mpin_cid" formatted="false" translatable="false">mpin_cid goes here</string>
<string name="mpin_backend" formatted="false" translatable="false">https://api.mpin.io</string>

Note that mpin_backend should always be https://api.mpin.io

Then add the url of your demo web app (configured using the web SDK, as per the previous section):

<!-- Custom service configuration -->
<string name="access_code_service_base_url" formatted="false" translatable="false">http://<demowebapp_url>/</string>

Then save and close the file.

You can now build the apk from the 'inappsample' folder. In Android Studio, select the 'inappsample' folder and click on the 'build' menu:

build_apk

Then choose 'Build APK' and the APK will be output to the inappsample/build/outputs/apk/ directory. This can then be installed for testing on an Android device.

You can now test the Android app by using it to register and login to your backend.


Android Demo App Overview


The following tree view illustrates the file structure of the demo application found in the inappsample subfolder:

treeview


SampleApplication.java


Here the SDK is intialized and the same instance is used throughout the application


Backend API interaction


In the /rest subfolder it can be seen that the app makes use of the available "/authzurl" endpoint on the backend application to retrieve an authorization url. This is then used in the app to obtain an access code which is then sent, along with the userId (email), back to the "/authtoken" endpoint to complete the authentication.


AccessCodeObtainingTask.java


This task makes use of rest/AccessCodeServiceApi.java to get the authorization url from your demo web app:

Response<AuthorizeUrlInfo> responseAuthUrl = accessCodeServiceApi.getAuthURL().execute();

and, subsequently, to get an access code:

com.miracl.mpinsdk.model.Status status = mfaSdk.getAccessCode(urlInfo.getAuthorizeUrl(), accessCodeContainer);

This task is then put to use in both RegisterUserActivity.java and LoginActivity.java, as explained below.


ValidateLoginTask.java


This gives a very simple example of how a validation with an access code can be done with a demo web app/service.

It also makes use of rest/AccessCodeServiceApi.java - this time to send an authorization token using the access code and user ID:

Response<ResponseBody> responseSetAuthToken = accessCodeServiceApi
              .setAuthToken(new AccessCodeInfo(mAuthCode, mUserId)).execute();

This task is then put to use in LoginActivity.java, as explained below.


RegisterUserActivity.java


This activity manages the initial user registration process. To begin registering the user will be asked to enter their email:

reg_user

The Submit Button

Within the private void onSubmitClick() code bracket the user's email address is obtained:

final String email = mEmailInput.getText().toString().trim();

A new AccessCodeObtainingTask is then begun which, if successful, begins onStartedRegistration() with the user's email. Within private void onStartedRegistration(final String email) it can be seen that makeNewUser() is used to create a new user object with the user's email:

mCurrentUser = SampleApplication.getMfaSdk().makeNewUser(email, "Android Sample App");

Note that the second variable which is passed with makeNewUser() is 'deviceName'. Here the text string has been entered manually, but some backends may require the device name to be captured, to use it later to determine which ID is associated with this device. It is also possible to use this method with just the first id variable being passed.

After we have a user, we can start the registration process for them. If successful this will trigger sending a confirmation email from the current backend:

return SampleApplication.getMfaSdk().startRegistration(mCurrentUser, SampleApplication.getCurrentAccessCode());

After this a getStatusCode() check is run to trigger the email sent message dialog:

mMessageDialog.show("Email has been sent to " + mCurrentUser.getId());

confirm_email

The Confirm button

Within the private void onConfirmClick() code bracket a first check is made that the mCurrentUser is not null. Then it is confirmed that the current user has followed the confirmation instructions in the registration email:

return SampleApplication.getMfaSdk().confirmRegistration(mCurrentUser);

Finally, if a getStatusCode() check returns OK the mEnterPinDialog is shown for the user to create their PIN:

if (status.getStatusCode() == com.miracl.mpinsdk.model.Status.Code.OK) {
    mEnterPinDialog.show();
};

Within the public void onPinEntered(final String pin) code bracket, finishRegistration() is used to pass the current user and their PIN to complete the registration process and set up their PIN:

return SampleApplication.getMfaSdk().finishRegistration(mCurrentUser, pin);

If the status code is returned as OK, then a new LoginActivity intent is started:

startActivity(new Intent(RegisterUserActivity.this, LoginActivity.class));


LoginActivity.java


login_activity

Within the private void configureSdkAndCurrentUser() code bracket, the mpin_cid and mpin_backend are fetched from the res/values/strings.xml file. If this is successful then getCurrentUserAndInit() is called to check for a registered user.

Within the private void getCurrentUserAndInit() code bracket the LoginActivity screen first will make a check to see if there is a registered user. It does this by creating ArrayLists and making use of the listUsers method in combination with using the getState() method to check if the user state is REGISTERED. It then checks that the user is registered with the current backend by first of all getting the backend url from the res/values/strings.xml file (String backendUrl = getString(R.string.mpin_backend);) and checks that this corresponds with the result from user.getBackend(). It finally shows the registered user information using initView():

mCurrentUser = currentBackendRegisteredUsers.get(0);
initView();

Within private void initView() it can be seen that the registered user info is displayed using the getId() getBackend() getState() and getCustomerId() methods. The delete (onDeleteClick and login (onLoginClick) buttons are also displayed here.

The Login button

In private void onLoginClick() you can see that a new AccessCodeObtainingTask is started in order to obtain the authorization url for the backend service. If successful the startLogin() method is called, which then begins an mEnterPinDialog().

Once the user has entered their pin, in public void onPinEntered(final String pin) it can be seen that, after using SampleApplication.getCurrentAccessCode(), the authentication process is started with startAuthentication(mCurrentUser, accessCode), which passes the user and access code to the server in order to obtain a time permit which confirms that the user is authorized to attempt authentication:

com.miracl.mpinsdk.model.Status startAuthenticationStatus = mPinMfa
        .startAuthentication(mCurrentUser, accessCode);

If this returns a status of OK then finishAuthentication(mCurrentUser, pin, accessCode, authCode) is used to finish authentication with the user's PIN:

return mPinMfa.finishAuthentication(mCurrentUser, pin, accessCode, authCode);

In onPostExecute(), if the status code for the user authentication is OK the validateLogin() method is called:

if (status.getStatusCode() == com.miracl.mpinsdk.model.Status.Code.OK && authCode != null) {
    validateLogin(authCode.toString());

In *private void validateLogin(String authCode) we can then see that, in order to validate the login, the backend service base url is obtained from the res/values/strings/xml file and allocated to a String clientService variable. It is then, along with the authCode and the current user's id/email, used to create a new ValidateLoginTask:

final String clientService = getString(R.string.access_code_service_base_url);
if (!clientService.isEmpty() && mCurrentUser != null && authCode != null) {
    new ValidateLoginTask(clientService, authCode, mCurrentUser.getId()) {

In onPostExecute you can then see that, if successful, getId() and getBackend() are used to display a successful login message dialog:

login_successful

Top