com.auth0.android.lock.PasswordlessLockActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.auth0.android.lock.PasswordlessLockActivity.java

Source

/*
 * LockPasswordlessActivity.java
 *
 * Copyright (c) 2016 Auth0 (http://auth0.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.auth0.android.lock;

import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;

import com.auth0.android.authentication.AuthenticationAPIClient;
import com.auth0.android.authentication.AuthenticationException;
import com.auth0.android.lock.adapters.Country;
import com.auth0.android.lock.errors.AuthenticationError;
import com.auth0.android.lock.errors.LoginErrorMessageBuilder;
import com.auth0.android.lock.events.CountryCodeChangeEvent;
import com.auth0.android.lock.events.FetchApplicationEvent;
import com.auth0.android.lock.events.OAuthLoginEvent;
import com.auth0.android.lock.events.PasswordlessLoginEvent;
import com.auth0.android.lock.internal.configuration.ApplicationFetcher;
import com.auth0.android.lock.internal.configuration.Configuration;
import com.auth0.android.lock.internal.configuration.Connection;
import com.auth0.android.lock.internal.configuration.Options;
import com.auth0.android.lock.internal.configuration.PasswordlessMode;
import com.auth0.android.lock.provider.AuthResolver;
import com.auth0.android.lock.views.PasswordlessLockView;
import com.auth0.android.provider.AuthCallback;
import com.auth0.android.provider.AuthProvider;
import com.auth0.android.provider.WebAuthProvider;
import com.auth0.android.result.Credentials;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;

import java.util.HashMap;
import java.util.List;

public class PasswordlessLockActivity extends AppCompatActivity
        implements ActivityCompat.OnRequestPermissionsResultCallback {

    private static final String TAG = PasswordlessLockActivity.class.getSimpleName();
    private static final int WEB_AUTH_REQUEST_CODE = 200;
    private static final int CUSTOM_AUTH_REQUEST_CODE = 201;
    private static final int PERMISSION_REQUEST_CODE = 202;
    private static final int COUNTRY_CODE_REQUEST_CODE = 120;
    private static final long RESULT_MESSAGE_DURATION = 3000;
    private static final long RESEND_TIMEOUT = 20 * 1000;
    private static final long CODE_TTL = 2 * 60 * 1000;

    private static final String LAST_PASSWORDLESS_TIME_KEY = "last_passwordless_time";
    private static final String LAST_PASSWORDLESS_EMAIL_NUMBER_KEY = "last_passwordless_email_number";
    private static final String LAST_PASSWORDLESS_COUNTRY_KEY = "last_passwordless_country";
    private static final String LAST_PASSWORDLESS_MODE_KEY = "last_passwordless_mode";
    private static final String LOCK_PREFERENCES_NAME = "Lock";
    private static final String COUNTRY_DATA_DIV = "@";

    private ApplicationFetcher applicationFetcher;
    private Configuration configuration;
    private Options options;
    private Handler handler;

    private PasswordlessLockView lockView;
    private LinearLayout passwordlessSuccessCover;
    private TextView resultMessage;

    private String lastPasswordlessEmailOrNumber;
    private Country lastPasswordlessCountry;
    private Bus lockBus;
    private ScrollView rootView;
    private TextView resendButton;

    private AuthProvider currentProvider;

    private LoginErrorMessageBuilder loginErrorBuilder;

    @SuppressWarnings("unused")
    public PasswordlessLockActivity() {
    }

    @VisibleForTesting
    PasswordlessLockActivity(Configuration configuration, Options options, PasswordlessLockView lockView,
            String lastEmailOrNumber) {
        this.configuration = configuration;
        this.options = options;
        this.lockView = lockView;
        this.lastPasswordlessEmailOrNumber = lastEmailOrNumber;
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (!hasValidLaunchConfig()) {
            return;
        }

        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        lockBus = new Bus();
        lockBus.register(this);
        handler = new Handler(getMainLooper());

        setContentView(R.layout.com_auth0_lock_activity_lock_passwordless);
        passwordlessSuccessCover = (LinearLayout) findViewById(R.id.com_auth0_lock_link_sent_cover);
        rootView = (ScrollView) findViewById(R.id.com_auth0_lock_content);
        resultMessage = (TextView) findViewById(R.id.com_auth0_lock_result_message);
        lockView = new PasswordlessLockView(this, lockBus, options.getTheme());
        RelativeLayout.LayoutParams lockViewParams = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        lockView.setLayoutParams(lockViewParams);
        rootView.addView(lockView);

        if (options.useCodePasswordless()) {
            loginErrorBuilder = new LoginErrorMessageBuilder(
                    R.string.com_auth0_lock_passwordless_code_request_error_message,
                    R.string.com_auth0_lock_passwordless_login_error_invalid_credentials_message);
        } else {
            loginErrorBuilder = new LoginErrorMessageBuilder(
                    R.string.com_auth0_lock_passwordless_link_request_error_message,
                    R.string.com_auth0_lock_passwordless_login_error_invalid_credentials_message);
        }
        lockBus.post(new FetchApplicationEvent());
    }

    private boolean hasValidLaunchConfig() {
        String errorDescription = null;
        if (!hasValidOptions()) {
            errorDescription = "Configuration is not valid and the Activity will finish.";
        }
        if (!hasValidTheme()) {
            errorDescription = "You need to use a Lock.Theme theme (or descendant) with this Activity.";
        }
        if (errorDescription == null) {
            return true;
        }
        Intent intent = new Intent(Constants.INVALID_CONFIGURATION_ACTION);
        intent.putExtra(Constants.ERROR_EXTRA, errorDescription);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
        finish();
        return false;
    }

    private boolean hasValidTheme() {
        TypedArray a = getTheme().obtainStyledAttributes(R.styleable.Lock_Theme);
        if (!a.hasValue(R.styleable.Lock_Theme_Auth0_HeaderLogo)) {
            a.recycle();
            return false;
        }
        return true;
    }

    private boolean hasValidOptions() {
        options = getIntent().getParcelableExtra(Constants.OPTIONS_EXTRA);
        if (options == null) {
            Log.e(TAG,
                    "Lock Options are missing in the received Intent and PasswordlessLockActivity will not launch. "
                            + "Use the PasswordlessLock.Builder to generate a valid Intent.");
            return false;
        }

        boolean launchedForResult = getCallingActivity() != null;
        if (launchedForResult) {
            Log.e(TAG, "You're not allowed to start Lock with startActivityForResult.");
            return false;
        }
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
            //TODO: Document this case for users on <= KITKAT, as they will not receive this warning.
            boolean launchedAsSingleTask = (getIntent().getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0;
            if (options.useBrowser() && !launchedAsSingleTask) {
                Log.e(TAG, "Please, check that you have specified launchMode 'singleTask' in the AndroidManifest.");
                return false;
            }
        }

        return true;
    }

    @Override
    public void onBackPressed() {
        boolean showingSuccessLayout = passwordlessSuccessCover.getVisibility() == View.VISIBLE;
        if (!showingSuccessLayout && lockView.onBackPressed()) {
            reloadRecentPasswordlessData();
            return;
        }
        if (!options.isClosable()) {
            return;
        }

        Log.v(TAG, "User has just closed the activity.");
        Intent intent = new Intent(Constants.CANCELED_ACTION);
        LocalBroadcastManager.getInstance(PasswordlessLockActivity.this).sendBroadcast(intent);
        super.onBackPressed();
    }

    private void deliverAuthenticationResult(Credentials credentials) {
        String requestedScopes = "openid"; //default authentication scope
        if (options.getAuthenticationParameters().containsKey("scope")) {
            requestedScopes = (String) options.getAuthenticationParameters().get("scope");
        }

        Intent intent = new Intent(Constants.AUTHENTICATION_ACTION);
        if (credentials.getAccessToken() == null) {
            intent.putExtra(Constants.ERROR_EXTRA, "The access_token is missing from the response.");
        } else if (requestedScopes.contains("openid") && credentials.getIdToken() == null) {
            intent.putExtra(Constants.ERROR_EXTRA, "The id_token is missing from the response.");
        } else if (requestedScopes.contains("offline_access") && credentials.getRefreshToken() == null) {
            intent.putExtra(Constants.ERROR_EXTRA, "The refresh_token is missing from the response.");
        } else {
            intent.putExtra(Constants.ID_TOKEN_EXTRA, credentials.getIdToken());
            intent.putExtra(Constants.ACCESS_TOKEN_EXTRA, credentials.getAccessToken());
            intent.putExtra(Constants.REFRESH_TOKEN_EXTRA, credentials.getRefreshToken());
            intent.putExtra(Constants.TOKEN_TYPE_EXTRA, credentials.getType());
        }

        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
        finish();
    }

    private void showErrorMessage(String message) {
        resultMessage.setBackgroundColor(
                ContextCompat.getColor(this, R.color.com_auth0_lock_result_message_error_background));
        resultMessage.setVisibility(View.VISIBLE);
        resultMessage.setText(message);
        lockView.showProgress(false);
        handler.removeCallbacks(resultMessageHider);
        handler.postDelayed(resultMessageHider, RESULT_MESSAGE_DURATION);
    }

    private Runnable resultMessageHider = new Runnable() {
        @Override
        public void run() {
            resultMessage.setVisibility(View.GONE);
        }
    };

    private void showLinkSentLayout() {
        TextView successMessage = (TextView) passwordlessSuccessCover
                .findViewById(R.id.com_auth0_lock_passwordless_message);
        successMessage.setText(String.format(getString(R.string.com_auth0_lock_title_passwordless_link_sent),
                lastPasswordlessEmailOrNumber));
        TextView gotCodeButton = (TextView) passwordlessSuccessCover.findViewById(R.id.com_auth0_lock_got_code);
        gotCodeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                passwordlessSuccessCover.setVisibility(View.GONE);
            }
        });
        resendButton = (TextView) passwordlessSuccessCover.findViewById(R.id.com_auth0_lock_resend);
        resendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                resendButton.setVisibility(View.GONE);
                rootView.removeView(lockView);
                lockView = new PasswordlessLockView(PasswordlessLockActivity.this, lockBus, options.getTheme());
                if (configuration != null) {
                    lockView.configure(configuration);
                    reloadRecentPasswordlessData();
                } else {
                    lockBus.post(new FetchApplicationEvent());
                }
                rootView.addView(lockView, ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT);
                passwordlessSuccessCover.setVisibility(View.GONE);
            }
        });
        passwordlessSuccessCover.setVisibility(View.VISIBLE);
        handler.removeCallbacks(resendTimeoutShower);
        handler.postDelayed(resendTimeoutShower, RESEND_TIMEOUT);
    }

    final Runnable resendTimeoutShower = new Runnable() {
        @Override
        public void run() {
            if (resendButton != null) {
                resendButton.setVisibility(View.VISIBLE);
            }
        }
    };

    private void reloadRecentPasswordlessData() {
        int choosenMode = configuration.getPasswordlessMode();
        if (choosenMode == PasswordlessMode.DISABLED) {
            return;
        }
        SharedPreferences sp = getSharedPreferences(LOCK_PREFERENCES_NAME, Context.MODE_PRIVATE);
        int savedMode = sp.getInt(LAST_PASSWORDLESS_MODE_KEY, PasswordlessMode.DISABLED);
        if (sp.getLong(LAST_PASSWORDLESS_TIME_KEY, 0) + CODE_TTL < System.currentTimeMillis()
                || choosenMode != savedMode) {
            Log.d(TAG, "Previous Passwordless data is too old to reload.");
            return;
        }

        String text = sp.getString(LAST_PASSWORDLESS_EMAIL_NUMBER_KEY, "");
        lastPasswordlessEmailOrNumber = text;
        String countryInfo = sp.getString(LAST_PASSWORDLESS_COUNTRY_KEY, null);
        if (countryInfo != null) {
            String isoCode = countryInfo.split(COUNTRY_DATA_DIV)[0];
            String dialCode = countryInfo.split(COUNTRY_DATA_DIV)[1];
            if (text.startsWith(dialCode)) {
                text = text.substring(dialCode.length());
            }
            lastPasswordlessCountry = new Country(isoCode, dialCode);
        }
        lockView.loadPasswordlessData(text, lastPasswordlessCountry);
    }

    private void persistRecentPasswordlessData(@NonNull String emailOrNumber, @Nullable Country country) {
        Log.v(TAG, "Saving recently used Passwordless data for the next time.");
        SharedPreferences sp = getSharedPreferences(LOCK_PREFERENCES_NAME, Context.MODE_PRIVATE);
        String countryData = country != null ? country.getIsoCode() + COUNTRY_DATA_DIV + country.getDialCode()
                : null;
        sp.edit().putLong(LAST_PASSWORDLESS_TIME_KEY, System.currentTimeMillis())
                .putString(LAST_PASSWORDLESS_EMAIL_NUMBER_KEY, emailOrNumber)
                .putString(LAST_PASSWORDLESS_COUNTRY_KEY, countryData)
                .putInt(LAST_PASSWORDLESS_MODE_KEY, configuration.getPasswordlessMode()).apply();
    }

    public void clearRecentPasswordlessData() {
        Log.v(TAG, "Deleting recent Passwordless data.");
        SharedPreferences sp = getSharedPreferences(LOCK_PREFERENCES_NAME, Context.MODE_PRIVATE);
        sp.edit().putLong(LAST_PASSWORDLESS_TIME_KEY, 0).putString(LAST_PASSWORDLESS_EMAIL_NUMBER_KEY, "")
                .putString(LAST_PASSWORDLESS_COUNTRY_KEY, null)
                .putInt(LAST_PASSWORDLESS_MODE_KEY, PasswordlessMode.DISABLED).apply();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        case COUNTRY_CODE_REQUEST_CODE:
            if (resultCode == RESULT_OK) {
                String country = data.getStringExtra(CountryCodeActivity.COUNTRY_CODE_EXTRA);
                String dialCode = data.getStringExtra(CountryCodeActivity.COUNTRY_DIAL_CODE_EXTRA);
                lockView.onCountryCodeSelected(country, dialCode);
            }
            break;
        case WEB_AUTH_REQUEST_CODE:
            lockView.showProgress(false);
            WebAuthProvider.resume(requestCode, resultCode, data);
            break;
        case CUSTOM_AUTH_REQUEST_CODE:
            lockView.showProgress(false);
            if (currentProvider != null) {
                currentProvider.authorize(requestCode, resultCode, data);
                currentProvider = null;
            }
            break;
        default:
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        lockView.showProgress(false);
        if (WebAuthProvider.resume(intent)) {
            return;
        } else if (currentProvider != null) {
            lockView.showProgress(false);
            currentProvider.authorize(intent);
            currentProvider = null;
            return;
        }

        //Passwordless result
        if (intent == null) {
            return;
        }
        if (configuration == null) {
            Log.w(TAG, String.format(
                    "Intent arrived with data %s but is going to be discarded as the Activity lacks of Configuration",
                    intent.getData()));
            return;
        }

        boolean useMagicLink = configuration.getPasswordlessMode() == PasswordlessMode.EMAIL_LINK
                || configuration.getPasswordlessMode() == PasswordlessMode.SMS_LINK;
        if (lastPasswordlessEmailOrNumber != null && useMagicLink) {
            String code = intent.getData().getQueryParameter("code");
            if (code == null || code.isEmpty()) {
                Log.w(TAG, "Passwordless Code is missing or could not be parsed");
                showErrorMessage(getString(R.string.com_auth0_lock_db_login_error_message));
                return;
            }
            PasswordlessLoginEvent event = PasswordlessLoginEvent.submitCode(configuration.getPasswordlessMode(),
                    code);
            onPasswordlessAuthenticationRequest(event);
        } else {
            Log.w(TAG, "Invalid Activity state");
        }

        super.onNewIntent(intent);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (currentProvider != null) {
            currentProvider.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
        }
    }

    @SuppressWarnings("unused")
    @Subscribe
    public void onFetchApplicationRequest(FetchApplicationEvent event) {
        if (applicationFetcher == null) {
            applicationFetcher = new ApplicationFetcher(options.getAccount(), new OkHttpClient());
            applicationFetcher.fetch(applicationCallback);
        }
    }

    @SuppressWarnings("unused")
    @Subscribe
    public void onCountryCodeChangeRequest(CountryCodeChangeEvent event) {
        Intent intent = new Intent(this, CountryCodeActivity.class);
        startActivityForResult(intent, COUNTRY_CODE_REQUEST_CODE);
    }

    @SuppressWarnings("unused")
    @Subscribe
    public void onPasswordlessAuthenticationRequest(PasswordlessLoginEvent event) {
        if (configuration.getPasswordlessConnection() == null) {
            Log.w(TAG, "There is no default Passwordless strategy to authenticate with");
            return;
        }

        lockView.showProgress(true);
        AuthenticationAPIClient apiClient = new AuthenticationAPIClient(options.getAccount());
        String connectionName = configuration.getPasswordlessConnection().getName();
        if (event.getCode() != null) {
            event.getLoginRequest(apiClient, lastPasswordlessEmailOrNumber)
                    .addAuthenticationParameters(options.getAuthenticationParameters())
                    .setConnection(connectionName).start(authCallback);
            return;
        }

        lastPasswordlessEmailOrNumber = event.getEmailOrNumber();
        lastPasswordlessCountry = event.getCountry();
        event.getCodeRequest(apiClient, connectionName).start(passwordlessCodeCallback);
    }

    @SuppressWarnings("unused")
    @Subscribe
    public void onOAuthAuthenticationRequest(OAuthLoginEvent event) {
        lastPasswordlessEmailOrNumber = null;
        lastPasswordlessCountry = null;
        Log.v(TAG, "Looking for a provider to use with the connection " + event.getConnection());
        currentProvider = AuthResolver.providerFor(event.getStrategy(), event.getConnection());
        if (currentProvider != null) {
            currentProvider.start(this, authProviderCallback, PERMISSION_REQUEST_CODE, CUSTOM_AUTH_REQUEST_CODE);
            return;
        }

        Log.d(TAG,
                "Couldn't find an specific provider, using the default: " + WebAuthProvider.class.getSimpleName());
        WebAuthProvider.init(options.getAccount()).useBrowser(options.useBrowser())
                .withConnectionScope(options.getConnectionsScope().get(event.getConnection()))
                .withParameters(options.getAuthenticationParameters()).withConnection(event.getConnection())
                .start(this, authProviderCallback, WEB_AUTH_REQUEST_CODE);
    }

    //Callbacks
    private com.auth0.android.callback.AuthenticationCallback<List<Connection>> applicationCallback = new com.auth0.android.callback.AuthenticationCallback<List<Connection>>() {
        @Override
        public void onSuccess(final List<Connection> connections) {
            configuration = new Configuration(connections, options);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    lockView.configure(configuration);
                    reloadRecentPasswordlessData();
                }
            });
            applicationFetcher = null;
        }

        @Override
        public void onFailure(final AuthenticationException error) {
            Log.e(TAG, "Failed to fetch the application: " + error.getMessage(), error);
            applicationFetcher = null;
            handler.post(new Runnable() {
                @Override
                public void run() {
                    lockView.configure(null);
                }
            });
        }
    };

    private com.auth0.android.callback.AuthenticationCallback<Void> passwordlessCodeCallback = new com.auth0.android.callback.AuthenticationCallback<Void>() {
        @Override
        public void onSuccess(Void payload) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    lockView.showProgress(false);
                    lockView.onPasswordlessCodeSent(lastPasswordlessEmailOrNumber);
                    if (!options.useCodePasswordless()) {
                        showLinkSentLayout();
                    }
                    persistRecentPasswordlessData(lastPasswordlessEmailOrNumber, lastPasswordlessCountry);
                }
            });
        }

        @Override
        public void onFailure(final AuthenticationException error) {
            Log.e(TAG, "Failed to request a passwordless Code/Link: " + error.getMessage(), error);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    String message = loginErrorBuilder.buildFrom(error).getMessage(PasswordlessLockActivity.this);
                    showErrorMessage(message);
                }
            });
        }
    };

    private com.auth0.android.callback.AuthenticationCallback<Credentials> authCallback = new com.auth0.android.callback.AuthenticationCallback<Credentials>() {
        @Override
        public void onSuccess(Credentials credentials) {
            clearRecentPasswordlessData();
            deliverAuthenticationResult(credentials);
        }

        @Override
        public void onFailure(final AuthenticationException error) {
            Log.e(TAG, "Failed to authenticate the user: " + error.getMessage(), error);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    showErrorMessage(loginErrorBuilder.buildFrom(error).getMessage(PasswordlessLockActivity.this));
                }
            });
        }
    };

    private AuthCallback authProviderCallback = new AuthCallback() {
        @Override
        public void onFailure(@NonNull final Dialog dialog) {
            Log.e(TAG, "Failed to authenticate the user. A dialog is going to be shown with more information.");
            handler.post(new Runnable() {
                @Override
                public void run() {
                    dialog.show();
                }
            });
        }

        @Override
        public void onFailure(final AuthenticationException exception) {
            final AuthenticationError authError = loginErrorBuilder.buildFrom(exception);
            final String message = authError.getMessage(PasswordlessLockActivity.this);
            ;
            Log.e(TAG, "Failed to authenticate the user: " + message, exception);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    showErrorMessage(message);
                }
            });
        }

        @Override
        public void onSuccess(@NonNull final Credentials credentials) {
            deliverAuthenticationResult(credentials);
        }
    };
}