FIDO Client Adapter
HYPR SDK for Android
Do This First
This article assumes you have already completed the HYPR SDK for Android Quick Start before continuing.
This article describes the steps for integrating the FIDO Client Adapter into an app to facilitate decentralized registration, authentication, and deregistration.
This implementation involves using HYPR SDK for Android to authenticate into an Android application. It is a wrapper around the FIDO Client that replaces the User Agent. It is used for implementations where the customer only wants HYPR to generate the raw FIDO payload. They will then take that raw FIDO payload and manage the server communication themselves. In this implementation there is no direct communication with a HYPR RP Server / HYPR FIDO Server.
Requirements
- The HYPR
.aar
files are in your Android project'sapp/libs
directory - Gradle version: 4.4
- Android Plugin version 3.1.3
minSdkVersion
23targetSdkVersion
28
SDK Build Tools
Since we're using an Android plugin for Gradle 3.0.0+, the project automatically uses a default version of the build tools specified by the plugin.
Update /app/<module_name>/build.gradle
/app/<module_name>/build.gradle
Include the HYPR AARs in your build.gradle
Once you have the HYPR
.aar
files in yourapp/libs
directory, make sure to add them in yourapp/<module_name>/build.gradle
file. An example of how to include them is at the bottom of the file below.
Following is an example of the FCA app/build.gradle
:
apply plugin: 'com.android.application'
apply plugin: 'org.sonarqube'
apply plugin: 'org.jetbrains.kotlin.android'
ext {
hyprVersion = "7.3.0"
hyprCode = 70300
cryptoVersion = "3.7.0"
adpVersion = "3.6.0"
sensorySmmaVersion = "4.1.2"
sensoryUtilsVersion = "4.1.2.4"
sensoryModelVersion = "2.1.0"
appId = "com.hypr.hyprfidoclientadaptersample"
keyLocation = "$rootProject.projectDir/app/fidoclientadapter.keystore"
keyStoreAlias = "HyprFidoClientAdapter"
keyStorePass = "wod82nfib9wq"
}
android {
compileSdkVersion 31
defaultConfig {
applicationId appId
minSdkVersion 23
targetSdkVersion 31
versionCode hyprCode
versionName hyprVersion
version versionName
vectorDrawables.useSupportLibrary = true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
project.archivesBaseName = "HyprFidoClientTest"
ndk {
abiFilters "armeabi", "armeabi-v7a", "arm64-v8a"
}
}
signingConfigs {
debug {
storeFile file(keyLocation)
storePassword keyStorePass
keyAlias keyStoreAlias
keyPassword keyStorePass
}
release {
storeFile file(keyLocation)
storePassword keyStorePass
keyAlias keyStoreAlias
keyPassword keyStorePass
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
minifyEnabled false
multiDexEnabled true
debuggable true
}
release {
minifyEnabled true
multiDexEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
multiDexKeepFile file('multidex-config.txt')
multiDexKeepProguard file('multidex-config.pro')
signingConfig signingConfigs.release
}
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
dexOptions {
javaMaxHeapSize "10g"
jumboMode = true
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// Android
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.appcompat:appcompat:1.3.1' // do not change yet - https://stackoverflow.com/questions/69033022/message-error-resource-androidattr-lstar-not-found
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.core:core-ktx:1.7.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.4.0'
implementation "androidx.security:security-crypto:1.0.0"
// GJON / POJO
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'org.apache.commons:commons-lang3:3.5'
// RxAndroid
implementation group: 'io.reactivex.rxjava2', name: 'rxandroid', version: '2.1.1'
implementation group: 'io.reactivex.rxjava2', name: 'rxjava', version: '2.2.21'
implementation 'javax.annotation:jsr250-api:1.0'
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3'
implementation(name: 'HyprCommonAdp', version: "${hyprVersion}", ext: 'aar')
implementation(name: 'HyprBiometricPromptAdp', version: "${hyprVersion}", ext: 'aar')
implementation(name: 'HyprPinAdp', version: "${hyprVersion}", ext: 'aar')
implementation(name: 'HyprFaceVoiceAdp', version: "${hyprVersion}", ext: 'aar')
implementation(name: 'HyprSilentAdp', version: "${hyprVersion}", ext: 'aar')
implementation(name: 'HyprPresenceAdp', version: "${hyprVersion}", ext: 'aar')
// Fingerprint
implementation 'androidx.biometric:biometric:1.1.0'
// Sensory SMMA
implementation(name: 'vvutils', version: "${sensoryUtilsVersion}", ext: 'aar')
implementation(name: 'datautils', version: "${sensoryUtilsVersion}", ext: 'aar')
implementation(name: 'smma', version: "${sensorySmmaVersion}", classifier: 'android', ext: 'aar')
implementation(name: 'model', version: "${sensoryModelVersion}", classifier: 'combiner', ext: 'aar')
implementation(name: 'model', version: "${sensoryModelVersion}", classifier: 'face-pnn', ext: 'aar')
implementation(name: 'model', version: "${sensoryModelVersion}", classifier: 'voice-tssv-udp_enUS', ext: 'aar')
// These dependencies are required by Sensory SDK
implementation 'com.parse.bolts:bolts-android:1.1.4'
implementation 'org.slf4j:slf4j-api:1.7.25'
implementation 'commons-io:commons-io:2.5'
implementation 'com.fasterxml.uuid:java-uuid-generator:3.1.4'
// These are also required by the SDK, but we are already using a newer version in our demo app
//implementation 'com.android.support:design:27.0.2'
//implementation 'org.apache.commons:commons-lang3:3.3.2'
// These are used in the Sensory SDK, but are NOT required. Since it's commented out, it won't be included
//implementation 'com.amazonaws:aws-android-sdk-core:2.2.10'
//implementation 'com.amazonaws:aws-android-sdk-s3:2.2.10'
// HYPR Crypto
implementation(name: "crypto", version: "${cryptoVersion}", ext: 'aar') { transitive = true }
// HYPR ADP
implementation(name: "THPAgent", version: "${adpVersion}", ext: 'aar') { transitive = true }
implementation(name: "TeeClient", version: "${adpVersion}", ext: 'aar') { transitive = true }
implementation(name: "caCrypto", version: "${adpVersion}", ext: 'aar') { transitive = true }
}
Basic Architecture
Interface | HyprInit FIDO Client Adapter |
Related Components | FIDO Client Adapter FIDO Client ASMs Authenticators |
Functionality | FIDO Operations |
HYPR Setup
HyprInit
and HyprFCA
must be initiated before any FIDO client calls are made. With that in mind, we suggest initializing during creation of the main FCA activity as described below. The entire FCA MainFCAActivity.java
file is shown here:
package com.hypr.hyprfidoclientadaptersample;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.navigation.NavigationView;
import androidx.core.content.FileProvider;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.os.StrictMode;
import android.text.TextUtils;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.hypr.hyprandroidcommon.authenticator.HyprPrefs;
import com.hypr.hyprandroidcommon.fidomodel.data.AaidKeys;
import com.hypr.hyprandroidcommon.test.HyprTestDataModel;
import com.hypr.hyprandroidcommon.uiadapter.adapters.HyprActionCallbacks;
import com.hypr.hyprandroidcommon.uiadapter.adapters.HyprActions;
import com.hypr.hyprandroidcommon.useragent.HyprInit;
import com.hypr.hyprandroidcommon.useragent.HyprSetup;
import com.hypr.hyprandroidcommon.useragent.data.datastruct.HyprGetRegistrationsData;
import com.hypr.hyprandroidcommon.useragent.data.error.HyprStatusExtraData;
import com.hypr.hyprandroidcommon.useragent.data.error.HyprStatusResult;
import com.hypr.hyprandroidcommon.useragent.fido.uaf.fidoclientadapter.HyprFCA;
import com.hypr.hyprandroidcommon.useragent.fido.uaf.fidoclientadapter.HyprFCACallback;
import com.hypr.hyprandroidcommon.utils.HyprSupportLog;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.hypr.hyprandroidcommon.useragent.data.datastruct.HyprGetRegistrationsData.EXTRA_DATA_KEY_GET_REGISTRATIONS;
public class MainFCAActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, AdapterView.OnItemSelectedListener {
private final String LOG_TAG = "HYPR_TEST_" + ((Object) this).getClass().getSimpleName();
// These contain one authenticator per selection
private final static String SAMPLE_FIDO_PAYLOAD_REG_SINGLES = "[{\"header\":{\"op\":\"Reg\",\"appID\":\"\",\"upv\":{\"major\":1,\"minor\":0},\"serverData\":\"HyprServer\"},\"challenge\":\"NHFkbTNoaDQ3NXBzazY0dWMyZGVnajNiZXU\",\"username\":\"User4e442oqsbi99g31u98rprrvbc3\",\"policy\":{\"accepted\":[[{\"aaid\":[\"0045#0022\"]}],[{\"aaid\":[\"0045#0112\"]}],[{\"aaid\":[\"0045#0122\"]}],[{\"aaid\":[\"0045#0052\"]}],[{\"aaid\":[\"0045#0012\"]}],[{\"aaid\":[\"0045#0032\"]}],[{\"aaid\":[\"0045#0033\"]}]],\"disallowed\":[{\"aaid\":[\"0045#0099\"]}]}}]";
private final static String SAMPLE_FIDO_PAYLOAD_AUTH_SINGLES = "[{\"header\":{\"op\":\"Auth\",\"appID\":\"\",\"upv\":{\"major\":1,\"minor\":0},\"serverData\":\"HyprServer\"},\"challenge\":\"dTk2dTNnNjY4djlmZXVxY3ZzMGplYmYzYnA\",\"policy\":{\"accepted\":[[{\"aaid\":[\"0045#0022\"]}],[{\"aaid\":[\"0045#0112\"]}],[{\"aaid\":[\"0045#0122\"]}],[{\"aaid\":[\"0045#0052\"]}],[{\"aaid\":[\"0045#0012\"]}],[{\"aaid\":[\"0045#0032\"]}],[{\"aaid\":[\"0045#0033\"]}]],\"disallowed\":[{\"aaid\":[\"0045#0099\"]}]}}]";
// These contain two authenticators per selection
private final static String SAMPLE_FIDO_PAYLOAD_REG_DOUBLES = "[{\"header\":{\"op\":\"Reg\",\"appID\":\"\",\"upv\":{\"major\":1,\"minor\":0},\"serverData\":\"HyprServer\"},\"challenge\":\"NHFkbTNoaDQ3NXBzazY0dWMyZGVnajNiZXU\",\"username\":\"User4e442oqsbi99g31u98rprrvbc3\",\"policy\":{\"accepted\":[[{\"aaid\":[\"0045#0052\"]},{\"aaid\":[\"0045#0012\"]}],[{\"aaid\":[\"0045#0052\"]},{\"aaid\":[\"0045#0032\"]}],[{\"aaid\":[\"0045#0012\"]},{\"aaid\":[\"0045#0032\"]}]]}}]";
private final static String SAMPLE_FIDO_PAYLOAD_AUTH_DOUBLES = "[{\"header\":{\"op\":\"Auth\",\"appID\":\"\",\"upv\":{\"major\":1,\"minor\":0},\"serverData\":\"HyprServer\"},\"challenge\":\"dTk2dTNnNjY4djlmZXVxY3ZzMGplYmYzYnA\",\"policy\":{\"accepted\":[[{\"aaid\":[\"0045#0012\"]},{\"aaid\":[\"0045#0052\"]}],[{\"aaid\":[\"0045#0032\"]},{\"aaid\":[\"0045#0052\"]}],[{\"aaid\":[\"0045#0012\"]},{\"aaid\":[\"0045#0032\"]}]]}}]";
// These are specific to the authenticator
private final static String SAMPLE_FIDO_PAYLOAD_DEREG_SINGLE = "[{\"authenticators\":[{\"aaid\":\"DEREG_AAID\",\"keyID\":\"DEREG_KEY\"}],\"header\":{\"op\":\"Dereg\",\"appID\":\"\",\"upv\":{\"major\":1,\"minor\":0}}}]";
private final static String SAMPLE_FIDO_PAYLOAD_DEREG_FINGER = "[{\"authenticators\":[{\"aaid\":\"0045#0052\",\"keyID\":\"bm96bi1WbVUtRklYZjVxU3RIM3Rmb3VyNHFZOW4wdEtza1lWNWJzaEhQNA\"}],\"header\":{\"op\":\"Dereg\",\"appID\":\"\",\"upv\":{\"major\":1,\"minor\":0}}}]";
private final static String SAMPLE_FIDO_PAYLOAD_DEREG_PIN = "[{\"authenticators\":[{\"aaid\":\"0045#0012\",\"keyID\":\"SjVYWXJUNXZSRzlQYUQ4bWQxeThoQm1PZmFTRXRaQ1ktSno5WjFNcEM2VQ\"}],\"header\":{\"op\":\"Dereg\",\"appID\":\"\",\"upv\":{\"major\":1,\"minor\":0}}}]";
private final static String SAMPLE_FIDO_PAYLOAD_DEREG_FACE = "[{\"authenticators\":[{\"aaid\":\"0045#0032\",\"keyID\":\"SjVYWXJUNXZSRzlQYUQ4bWQxeThoQm1PZmFTRXRaQ1ktSno5WjFNcEM2VQ\"}],\"header\":{\"op\":\"Dereg\",\"appID\":\"\",\"upv\":{\"major\":1,\"minor\":0}}}]";
private final static String SAMPLE_FIDO_PAYLOAD_DEREG_VOICE = "[{\"authenticators\":[{\"aaid\":\"0045#0033\",\"keyID\":\"SjVYWXJUNXZSRzlQYUQ4bWQxeThoQm1PZmFTRXRaQ1ktSno5WjFNcEM2VQ\"}],\"header\":{\"op\":\"Dereg\",\"appID\":\"\",\"upv\":{\"major\":1,\"minor\":0}}}]";
public static final List<String> FIDO_USERNAMES = Collections.unmodifiableList(Arrays.asList("User4e442oqsbi99g31u98rprrvbc3", "User55532683258946082357845555", "User666qsbi99g31u98rprrvbc6666"));
private void setUserProfilePosition(final int userProfilePosition) {
HyprPrefs.setSettingsParam(this, "HYPR_FCA_USER_PROFILE_POSITION", userProfilePosition);
}
private int getUserProfilePosition() {
return HyprPrefs.getSettingsParam(this, "HYPR_FCA_USER_PROFILE_POSITION", 0);
}
@Override
public void onItemSelected(AdapterView<?> parent,
View view,
int position,
long id) {
// User Profile Spinner
Log.d(LOG_TAG, "onItemSelected position: " + position);
setUserProfilePosition(position);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// User Profile Spinner
}
private String getRegPayload() {
String payload = SAMPLE_FIDO_PAYLOAD_REG_SINGLES;
int userProfilePosition = getUserProfilePosition();
String fidoUsername = FIDO_USERNAMES.get(userProfilePosition);
Log.d(LOG_TAG, "getRegPayload userProfilePosition: " + userProfilePosition + " fidoUsername: " + fidoUsername);
payload = payload.replace("User4e442oqsbi99g31u98rprrvbc3", fidoUsername);
return payload;
}
private String getAuthPayload() {
String payload = SAMPLE_FIDO_PAYLOAD_AUTH_SINGLES;
return payload;
}
private String getDeregPayload() {
String payload = SAMPLE_FIDO_PAYLOAD_DEREG_SINGLE;
// This will deregister the first aaid and key that is in the mapping
if (mGetRegistrationsData.getAaidKeys().size() > 0) {
AaidKeys aaidKeys = mGetRegistrationsData.getAaidKeys().get(0);
String aaid = aaidKeys.getAaid();
String key = "";
if (!TextUtils.isEmpty(aaid)) {
List<String> keys = aaidKeys.getKeyIds();
if (keys.size() > 0) {
key = keys.get(0);
payload = payload.replace("DEREG_AAID", aaid);
payload = payload.replace("DEREG_KEY", key);
}
}
}
return payload;
}
private Spinner mUserProfileSpinner;
private boolean mIsSuccess;
private boolean mIsUiEnabled;
private String mFidoPayloadToSendToServer;
private String mResultToString;
private String mErrorCode;
private String mErrorDisplayText;
private HyprGetRegistrationsData mGetRegistrationsData;
private View mSpinnerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(LOG_TAG, "onCreate");
setContentView(R.layout.activity_main_fca);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
mIsUiEnabled = false;
mIsSuccess = false;
mFidoPayloadToSendToServer = "";
mResultToString = "";
mErrorCode = "";
mErrorDisplayText = "";
mGetRegistrationsData = new HyprGetRegistrationsData();
LinearLayout initView = findViewById(R.id.layout_initializing);
initView.setVisibility(View.VISIBLE);
// Initialize ADP/TrustData
HyprInit.getInstance().initTrustData(this, new HyprInit.InitTrustDataCallback() {
@Override
public void onInstallComplete() {
Log.d(LOG_TAG, "onCreate onInstallComplete");
// Initialize the FidoClientAdapter
HyprFCA.getInstance().init(MainFCAActivity.this, true);
mIsUiEnabled = true;
LinearLayout initView = findViewById(R.id.layout_initializing);
initView.setVisibility(View.GONE);
// as debug build look for:
// installOrUpdateTA:TEE
// for type of ADP installation: TEE/SWP
}
@Override
public void onInstallError(@NonNull String error,
@Nullable Throwable throwable) {
Log.e(LOG_TAG, "onCreate onInstallError error: " + error, throwable);
LinearLayout initView = findViewById(R.id.layout_initializing);
TextView initText = findViewById(R.id.text_initializing);
initText.setText("ADP ERROR ON INIT: " + error);
// you can choose to check the throwable instance for this custom HYPR Exception:
// PackageNameMismatchException
//
// Sample Code:
// if (throwable instanceof PackageNameMismatchException) { ... }
//
// which indicates that you are using the incorrect ADP AARs.
// if it is this exception class, then you can handle it in a customized manner.
}
});
}
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
int id = item.getItemId();
if (mIsUiEnabled) {
if (id == R.id.nav_upgrade) {
HyprFCA.getInstance().upgradeSingleUserToMultiUser(this, FIDO_USERNAMES.get(getUserProfilePosition()));
} else if (id == R.id.nav_get_registrations) {
mSpinnerView = displaySpinner();
HyprFCA.getInstance().getRegistrations(this, getRegPayload(), getGetRegistrationsCallback(), FIDO_USERNAMES.get(getUserProfilePosition()));
} else if (id == R.id.nav_reg) {
mSpinnerView = displaySpinner();
HyprFCA.getInstance().registerRequest(this, getRegPayload(), getRegisterCallback());
} else if (id == R.id.nav_auth) {
mSpinnerView = displaySpinner();
HyprFCA.getInstance().authenticateRequest(this, getAuthPayload(), getAuthenticateCallback(), FIDO_USERNAMES.get(getUserProfilePosition()));
} else if (id == R.id.nav_dereg) {
mSpinnerView = displaySpinner();
HyprFCA.getInstance().deregister(this, getDeregPayload(), getDeregisterCallback(), FIDO_USERNAMES.get(getUserProfilePosition()));
} else if (id == R.id.nav_reset_app) {
mSpinnerView = displaySpinner();
HyprFCA.getInstance().reset(this, getResetCallback(), FIDO_USERNAMES.get(getUserProfilePosition()));
} else if (id == R.id.nav_send_support_log) {
String supportEmail = "Log File Attached";
String[] supportEmails = new String[1];
supportEmails[0] = supportEmail;
String chooserTitle = "Support Log Email";
File logFile = HyprSupportLog.getLogsAsFile(this);
Uri uri = FileProvider.getUriForFile(this, this.getString(R.string.fileprovider_authority), logFile);
ArrayList<Uri> uris = new ArrayList<Uri>();
uris.add(uri);
StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder().build();
StrictMode.setVmPolicy(policy);
Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.setDataAndType(Uri.parse("mailto"), "text/plain");
intent.putExtra(Intent.EXTRA_EMAIL, supportEmails);
intent.putExtra(Intent.EXTRA_SUBJECT, "Support Log");
intent.putExtra(Intent.EXTRA_TEXT, "Support Log Attached");
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
startActivity(Intent.createChooser(intent, chooserTitle));
}
} else {
Log.d(LOG_TAG, "onNavigationItemSelected ADP has not finished initializing yet.");
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
@Override
protected void onResume() {
Log.d(LOG_TAG, "onResume");
super.onResume();
if (android.os.Build.VERSION.SDK_INT < 29) {
onActivityResume();
}
}
@Override
public void onTopResumedActivityChanged(boolean onTop) {
Log.d(LOG_TAG, "onTopResumedActivityChanged onTop: " + onTop);
if (onTop) {
onActivityResume();
}
}
/**
* Due to changes made to the Activity Lifecycle in Android 10, we can no longer rely on onResume() to know when an
* activity is resumed and in the foreground. onActivityResume() should be used instead of onResume()
*/
protected void onActivityResume() {
Log.d(LOG_TAG, "onActivityResume");
Spinner spinnerUserProfile = findViewById(R.id.spinner_user_profiles);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.user_profile_names_array, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerUserProfile.setAdapter(adapter);
spinnerUserProfile.setOnItemSelectedListener(this);
spinnerUserProfile.setSelection(getUserProfilePosition());
TextView textViewSuccess = findViewById(R.id.textview_result_is_success);
textViewSuccess.setText(Boolean.toString(mIsSuccess));
TextView textViewFidoPayload = findViewById(R.id.textview_result_fido_payload);
textViewFidoPayload.setText(mFidoPayloadToSendToServer);
TextView textViewErrorCode = findViewById(R.id.textview_error_code_result);
textViewErrorCode.setText(mErrorCode);
TextView textViewErrorDisplayText = findViewById(R.id.textview_error_display_text_result);
textViewErrorDisplayText.setText(mErrorDisplayText);
TextView textViewResult = findViewById(R.id.textview_result_to_string);
textViewResult.setText(mResultToString);
Toolbar toolbar = findViewById(R.id.toolbar);
String toolbarText = getString(R.string.app_name) + " (v" + BuildConfig.VERSION_NAME + ")";
toolbar.setTitle(toolbarText);
}
private HyprFCACallback getGetRegistrationsCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCompleteCallback onSuccess");
mIsSuccess = true;
mFidoPayloadToSendToServer = hyprStatusResult.getSuccessPayload();
mResultToString = hyprStatusResult.toString();
mErrorCode = "";
mErrorDisplayText = "";
Map<String, HyprStatusExtraData.HyprAuthExtraData> extraDataMap = hyprStatusResult.getStatusExtraData().getAuthExtraData();
mGetRegistrationsData = (HyprGetRegistrationsData) extraDataMap.get(EXTRA_DATA_KEY_GET_REGISTRATIONS);
// cleanup at end of Fido Operation sequence
cleanup();
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCompleteCallback onFail");
// cleanup at end of Fido Operation sequence
cleanup();
}
};
}
private HyprFCACallback getRegisterCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCallback onSuccess");
// updating ui data
mIsSuccess = true;
mFidoPayloadToSendToServer = hyprStatusResult.getSuccessPayload();
mResultToString = hyprStatusResult.toString();
mErrorCode = "";
mErrorDisplayText = "";
// TODO: Process the payload
// TODO: Send to Fido server
// TODO: Get real results back from server
boolean resultsFromServer = true;
// send register complete
HyprFCA.getInstance().registerComplete(MainFCAActivity.this, resultsFromServer, getRegisterCompleteCallback());
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCompleteCallback onFail");
// updating ui data
mIsSuccess = false;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = String.valueOf(hyprStatusResult.getLowestLevelDisplayCode());
mErrorDisplayText = hyprStatusResult.getLowestLevelDisplayText();
// send register complete
HyprFCA.getInstance().registerComplete(MainFCAActivity.this, false, getRegisterCompleteCallback());
}
};
}
private HyprFCACallback getRegisterCompleteCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCompleteCallback onSuccess");
// cleanup at end of Fido Operation sequence
cleanup();
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCompleteCallback onFail");
// cleanup at end of Fido Operation sequence
cleanup();
}
};
}
private HyprFCACallback getAuthenticateCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getAuthenticateCallback onSuccess");
// updating ui data
mIsSuccess = true;
mResultToString = hyprStatusResult.toString();
mFidoPayloadToSendToServer = hyprStatusResult.getSuccessPayload();
mErrorCode = "";
mErrorDisplayText = "";
// TODO: Process the payload
// TODO: Send to Fido server
// TODO: Get real results back from server
boolean resultsFromServer = true;
// send authenticate complete
HyprFCA.getInstance().authenticateComplete(MainFCAActivity.this, resultsFromServer, getAuthenticateCompleteCallback());
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getAuthenticateCallback onFail");
// updating ui data
mIsSuccess = false;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = String.valueOf(hyprStatusResult.getLowestLevelDisplayCode());
mErrorDisplayText = hyprStatusResult.getLowestLevelDisplayText();
// send authenticate complete
HyprFCA.getInstance().authenticateComplete(MainFCAActivity.this, false, getAuthenticateCompleteCallback());
}
};
}
private HyprFCACallback getAuthenticateCompleteCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getAuthenticateCompleteCallback onSuccess");
// cleanup at end of Fido Operation sequence
cleanup();
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getAuthenticateCompleteCallback onFail");
// cleanup at end of Fido Operation sequence
cleanup();
}
};
}
private HyprFCACallback getDeregisterCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getDeregisterCallback onSuccess");
// updating ui data
mIsSuccess = true;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = "";
mErrorDisplayText = "";
// cleanup at end of Fido Operation sequence
cleanup();
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getDeregisterCallback onFail");
// updating ui data
mIsSuccess = false;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = String.valueOf(hyprStatusResult.getLowestLevelDisplayCode());
mErrorDisplayText = hyprStatusResult.getLowestLevelDisplayText();
// cleanup at end of Fido Operation sequence
cleanup();
}
};
}
private HyprFCACallback getResetCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getResetCallback onSuccess");
// updating ui data
mIsSuccess = true;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = "";
mErrorDisplayText = "";
// cleanup at end of Operation sequence
cleanup();
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getResetCallback onFail");
// updating ui data
mIsSuccess = false;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = String.valueOf(hyprStatusResult.getLowestLevelDisplayCode());
mErrorDisplayText = hyprStatusResult.getLowestLevelDisplayText();
// cleanup at end of Operation sequence
cleanup();
}
};
}
private void cleanup() {
Log.d(LOG_TAG, "cleanup");
HyprFCA.getInstance().cleanup(MainFCAActivity.this);
if (mSpinnerView != null) {
mSpinnerView.setVisibility(View.GONE);
}
}
public View displaySpinner() {
@SuppressLint("InflateParams") View progressBar = getLayoutInflater().inflate(R.layout.hypr_common_view_progress_spinner, null);
ViewGroup.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
addContentView(progressBar, layoutParams);
return progressBar;
}
}
Unintended behavior may occur when backgrounding the app during registration, authentication, deregistration, etc. To prevent this behavior, we have created app/src/main/java/com/hypr/hyprfidoclientadaptersample/App.java
to extend MultiDexApplication
.
package com.hypr.hyprfidoclientadaptersample;
import androidx.annotation.Keep;
import androidx.multidex.MultiDexApplication;
@Keep
public final class App extends MultiDexApplication {
@Override
public void onCreate() {
super.onCreate();
}
}
Include .App
for the android:name
in AndroidManifest.xml
.
<manifest>
<application
// ...
android:name=".App" android:extractNativeLibs="true" android:roundIcon="@mipmap/hypr_app_icon_round" android:label="@string/app_name" android:icon="@mipmap/hypr_app_icon" android:allowBackup="false" android:exported="true"
// ...
</manifest>
Testing Reg/Auth/Dereg
The onNavigationItemSelected
menu determines the action taken:
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
int id = item.getItemId();
if (mIsUiEnabled) {
if (id == R.id.nav_upgrade) {
HyprFCA.getInstance().upgradeSingleUserToMultiUser(this, FIDO_USERNAMES.get(getUserProfilePosition()));
} else if (id == R.id.nav_get_registrations) {
mSpinnerView = displaySpinner();
HyprFCA.getInstance().getRegistrations(this, getRegPayload(), getGetRegistrationsCallback(), FIDO_USERNAMES.get(getUserProfilePosition()));
} else if (id == R.id.nav_reg) {
mSpinnerView = displaySpinner();
HyprFCA.getInstance().registerRequest(this, getRegPayload(), getRegisterCallback());
} else if (id == R.id.nav_auth) {
mSpinnerView = displaySpinner();
HyprFCA.getInstance().authenticateRequest(this, getAuthPayload(), getAuthenticateCallback(), FIDO_USERNAMES.get(getUserProfilePosition()));
} else if (id == R.id.nav_dereg) {
mSpinnerView = displaySpinner();
HyprFCA.getInstance().deregister(this, getDeregPayload(), getDeregisterCallback(), FIDO_USERNAMES.get(getUserProfilePosition()));
} else if (id == R.id.nav_reset_app) {
mSpinnerView = displaySpinner();
HyprFCA.getInstance().reset(this, getResetCallback(), FIDO_USERNAMES.get(getUserProfilePosition()));
} else if (id == R.id.nav_send_support_log) {
String supportEmail = "Log File Attached";
String[] supportEmails = new String[1];
supportEmails[0] = supportEmail;
String chooserTitle = "Support Log Email";
File logFile = HyprSupportLog.getLogsAsFile(this);
Uri uri = FileProvider.getUriForFile(this, this.getString(R.string.fileprovider_authority), logFile);
ArrayList<Uri> uris = new ArrayList<Uri>();
uris.add(uri);
StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder().build();
StrictMode.setVmPolicy(policy);
Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.setDataAndType(Uri.parse("mailto"), "text/plain");
intent.putExtra(Intent.EXTRA_EMAIL, supportEmails);
intent.putExtra(Intent.EXTRA_SUBJECT, "Support Log");
intent.putExtra(Intent.EXTRA_TEXT, "Support Log Attached");
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
startActivity(Intent.createChooser(intent, chooserTitle));
}
} else {
Log.d(LOG_TAG, "onNavigationItemSelected ADP has not finished initializing yet.");
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
FIDO Payloads
Commands from
HyprFCA
can be used to process FIDO payloads.
Register
The following snippet represents the registration payload called during the menu choices:
// Registration Payload
private String getRegPayload() {
String payload = SAMPLE_FIDO_PAYLOAD_REG_SINGLES;
int userProfilePosition = getUserProfilePosition();
String fidoUsername = FIDO_USERNAMES.get(userProfilePosition);
Log.d(LOG_TAG, "getRegPayload userProfilePosition: " + userProfilePosition + " fidoUsername: " + fidoUsername);
payload = payload.replace("User4e442oqsbi99g31u98rprrvbc3", fidoUsername);
return payload;
}
// ...
Registration requires a callback to handle the response.
// ...
private HyprFCACallback getGetRegistrationsCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCompleteCallback onSuccess");
mIsSuccess = true;
mFidoPayloadToSendToServer = hyprStatusResult.getSuccessPayload();
mResultToString = hyprStatusResult.toString();
mErrorCode = "";
mErrorDisplayText = "";
Map<String, HyprStatusExtraData.HyprAuthExtraData> extraDataMap = hyprStatusResult.getStatusExtraData().getAuthExtraData();
mGetRegistrationsData = (HyprGetRegistrationsData) extraDataMap.get(EXTRA_DATA_KEY_GET_REGISTRATIONS);
// cleanup at end of Fido Operation sequence
cleanup();
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCompleteCallback onFail");
// cleanup at end of Fido Operation sequence
cleanup();
}
};
}
private HyprFCACallback getRegisterCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCallback onSuccess");
// updating ui data
mIsSuccess = true;
mFidoPayloadToSendToServer = hyprStatusResult.getSuccessPayload();
mResultToString = hyprStatusResult.toString();
mErrorCode = "";
mErrorDisplayText = "";
// TODO: Process the payload
// TODO: Send to Fido server
// TODO: Get real results back from server
boolean resultsFromServer = true;
// send register complete
HyprFCA.getInstance().registerComplete(MainFCAActivity.this, resultsFromServer, getRegisterCompleteCallback());
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCompleteCallback onFail");
// updating ui data
mIsSuccess = false;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = String.valueOf(hyprStatusResult.getLowestLevelDisplayCode());
mErrorDisplayText = hyprStatusResult.getLowestLevelDisplayText();
// send register complete
HyprFCA.getInstance().registerComplete(MainFCAActivity.this, false, getRegisterCompleteCallback());
}
};
}
private HyprFCACallback getRegisterCompleteCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCompleteCallback onSuccess");
// cleanup at end of Fido Operation sequence
cleanup();
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getRegisterCompleteCallback onFail");
// cleanup at end of Fido Operation sequence
cleanup();
}
};
}
Authenticate
The following snippet represents the authentication payload called during the menu choices:
private String getAuthPayload() {
String payload = SAMPLE_FIDO_PAYLOAD_AUTH_SINGLES;
return payload;
}
Authenticate requires a callback to handle the response.
private HyprFCACallback getAuthenticateCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getAuthenticateCallback onSuccess");
// updating ui data
mIsSuccess = true;
mResultToString = hyprStatusResult.toString();
mFidoPayloadToSendToServer = hyprStatusResult.getSuccessPayload();
mErrorCode = "";
mErrorDisplayText = "";
// TODO: Process the payload
// TODO: Send to Fido server
// TODO: Get real results back from server
boolean resultsFromServer = true;
// send authenticate complete
HyprFCA.getInstance().authenticateComplete(MainFCAActivity.this, resultsFromServer, getAuthenticateCompleteCallback());
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getAuthenticateCallback onFail");
// updating ui data
mIsSuccess = false;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = String.valueOf(hyprStatusResult.getLowestLevelDisplayCode());
mErrorDisplayText = hyprStatusResult.getLowestLevelDisplayText();
// send authenticate complete
HyprFCA.getInstance().authenticateComplete(MainFCAActivity.this, false, getAuthenticateCompleteCallback());
}
};
}
private HyprFCACallback getAuthenticateCompleteCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getAuthenticateCompleteCallback onSuccess");
// cleanup at end of Fido Operation sequence
cleanup();
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getAuthenticateCompleteCallback onFail");
// cleanup at end of Fido Operation sequence
cleanup();
}
};
}
Deregister
The following snippet represents the deregistration payload called during the menu choices:
private String getDeregPayload() {
String payload = SAMPLE_FIDO_PAYLOAD_DEREG_SINGLE;
// This will deregister the first aaid and key that is in the mapping
if (mGetRegistrationsData.getAaidKeys().size() > 0) {
AaidKeys aaidKeys = mGetRegistrationsData.getAaidKeys().get(0);
String aaid = aaidKeys.getAaid();
String key = "";
if (!TextUtils.isEmpty(aaid)) {
List<String> keys = aaidKeys.getKeyIds();
if (keys.size() > 0) {
key = keys.get(0);
payload = payload.replace("DEREG_AAID", aaid);
payload = payload.replace("DEREG_KEY", key);
}
}
}
return payload;
}
Deregistration requires a callback to handle the response.
private HyprFCACallback getDeregisterCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getDeregisterCallback onSuccess");
// updating ui data
mIsSuccess = true;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = "";
mErrorDisplayText = "";
// cleanup at end of Fido Operation sequence
cleanup();
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getDeregisterCallback onFail");
// updating ui data
mIsSuccess = false;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = String.valueOf(hyprStatusResult.getLowestLevelDisplayCode());
mErrorDisplayText = hyprStatusResult.getLowestLevelDisplayText();
// cleanup at end of Fido Operation sequence
cleanup();
}
};
}
Reset FIDO Client Adapter
FCA Reset
You can use a command from the
HyprFidoClientAdapter
to reset the FIDO Client Adapter. This will clear the FIDO Client Adapter database of all authenticators and states.
To reset the FCA completely:
private HyprFCACallback getResetCallback() {
return new HyprFCACallback() {
@Override
public void onSuccess(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getResetCallback onSuccess");
// updating ui data
mIsSuccess = true;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = "";
mErrorDisplayText = "";
// cleanup at end of Operation sequence
cleanup();
}
@Override
public void onFail(@NonNull HyprStatusResult hyprStatusResult) {
Log.d(LOG_TAG, "getResetCallback onFail");
// updating ui data
mIsSuccess = false;
mFidoPayloadToSendToServer = "";
mResultToString = hyprStatusResult.toString();
mErrorCode = String.valueOf(hyprStatusResult.getLowestLevelDisplayCode());
mErrorDisplayText = hyprStatusResult.getLowestLevelDisplayText();
// cleanup at end of Operation sequence
cleanup();
}
};
}
Offline Mode
When there is no network, Offline Mode will be enabled. We determine this by:
((ConnectivityManager) activity.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo().isConnected() == false
-
In Offline Mode, a failure will be returned when:
- There is no response cache
- There is cache, but the
facetId
cannot be verified
-
In Online Mode, any network error will return a failure
Error Handling and HyprStatusResult
HyprStatusResult
See Error Handling for more details on HyprStatusResult
, HyprDisplayResult
, and HyprDisplayCodes
.
Localization
The app/src/main/res/values/strings.xml
file in the Sample App contains all of the overridable strings.
<!-- Loading Screen -->
<string name="hypr_loading_button_skip">Skip</string>
<string name="hypr_loading_button_learn">Learn About This App</string>
<!-- Begin Screen -->
<string name="hypr_begin_text_description">True Password-less Security\nPowered By\nDecentralized Authentication</string>
<string name="hypr_begin_button_lets_begin">Let\'s Begin</string>
<!-- Selection License Screen -->
<string name="hypr_selection_text_description">To complete setup please scan QR code or input 6-digit PIN to pair your mobile device</string>
<string name="hypr_selection_text_qr">SCAN QR</string>
<string name="hypr_selection_text_info">ABOUT HYPR</string>
<string name="hypr_selection_text_pin">ENTER PIN</string>
<!-- Info Screen -->
<string name="hypr_settings_manage">Manage</string>
<string name="hypr_powered_by">POWERED BY</string>
<string name="hypr_version">APP VERSION </string>
<string name="hypr_contact_us">Contact Us</string>
<string name="hypr_support">Support</string>
<!-- Oob license accepted -->
<string name="hypr_oob_license_accepted_title">Stay tuned…</string>
<string name="hypr_oob_license_accepted_description">We are building your personalized experience</string>
<!-- Browser Main Screen -->
<string name="hypr_main_browser_instructions">Begin your demo experience on the web at</string>
<string name="hypr_main_browser_instructions_1">Login to an online banking portal with Multi-Factor Biometrics.</string>
<string name="hypr_main_browser_instructions_2">Approve a wire transfer with FIDO-certified Step-up Authentication.</string>
<string name="hypr_main_browser_instructions_3">Learn how HYPR decentralized authentication eliminates credential reuse, prevents phishing and minimizes the risk of a breach.</string>
<string name="hypr_main_browser_options">On the website, you can demo a secure password-less experience powered by HYPR:</string>
<string name="hypr_main_browser_bank_url">demo.hypr.com</string>
<string name="hypr_main_browser_login_successful_text">Login Successful</string>
<string name="hypr_action_add_account">Add Account</string>
<string name="hypr_main_browser_landing_screen_title">True-Passwordless Authentication</string>
<string name="hypr_main_browser_landing_screen_subtitle">From HYPR</string>
<!-- Workstation Main Screen -->
<string name="hypr_main_workstation_title">Paired Workstations</string>
<!-- Workstation Main No Internet Screen -->
<string name="hypr_main_workstation_no_internet_one">Seems like you have no internet connection…</string>
<string name="hypr_main_workstation_no_internet_two">Make sure you are connected to the internet and try again</string>
<string name="hypr_main_workstation_no_internet_popup">No internet connection available</string>
<string name="hypr_main_workstation_no_internet_popup_reconnecting">Reconnecting…</string>
<!-- Workstation No Internet Screen -->
<string name="hypr_workstation_no_internet_dialog_title">No Internet Connection</string>
<string name="hypr_workstation_no_internet_dialog_message_delete">Could not delete selected workstation(s)… please check your internet connection and try again</string>
<string name="hypr_workstation_no_internet_dialog_message_add_workstation">Could not add workstation… please check your internet connection and try again</string>
<string name="hypr_workstation_no_internet_dialog_message_support">Support email could not be sent… please check your internet connection and try again</string>
<string name="hypr_workstation_no_internet_dialog_message_reset">Could not reset application… please check your internet connection and try again</string>
<string name="hypr_workstation_no_internet_dialog_message_workstation">Please check your internet connection and try again</string>
<!-- Workstation Main None Screen -->
<string name="hypr_main_workstation_none_add">Add a Workstation</string>
<!-- Workstation Main Single Screen -->
<string name="hypr_main_workstation_single_add">Add More Workstations</string>
<string name="hypr_main_workstation_single_unlock">Tap to Unlock or Long Press for More Options</string>
<!-- Browser Success Overlay Screen -->
<string name="hypr_common_nicely_done_text">Nicely Done.</string>
<string name="hypr_common_logged_in_welcome_text_long">You have successfully enrolled your decentralized biometrics and registered your trusted device with the Highlands Bank Demo Experience</string>
<!-- Workstation Success Overlay Screen -->
<string name="hypr_workstation_license_enroll_success_title">Nicely done.</string>
<string name="hypr_workstation_license_enroll_success_text">Your mobile device is now paired with your workstation.</string>
<string name="hypr_workstation_added_success_text">Added</string>
<string name="hypr_workstation_unlocked_success_text">Unlocked</string>
<string name="hypr_workstation_delete_success_text">Workstation Deleted</string>
<string name="hypr_workstation_delete_multiple_success_text">Workstations Deleted</string>
<string name="hypr_workstation_edit_success_text">Edit Successful</string>
<!-- Workstation Fail Overlay Screen -->
<string name="hypr_workstation_try">TRY</string>
<string name="hypr_workstation_again">AGAIN</string>
<string name="hypr_workstation_edit_fail_text">Edit Failed</string>
<string name="hypr_workstation_already_exists">This computer is already paired.</string>
<string name="hypr_workstation_max_limit_fail_text">You have reached the limit of paired workstations. Please remove unused workstations and try again.</string>
<string name="hypr_workstation_logged_out_state_error">You must be logged in before you can unlock.</string>
<!-- Workstation Config Overlay Screen -->
<string name="hypr_overlay_config_edit">EDIT</string>
<string name="hypr_overlay_config_delete">DELETE</string>
<string name="hypr_overlay_config_history">HISTORY</string>
<!-- Workstation Status Update Overlay Screen -->
<string name="hypr_workstation_status_update">This workstation is already unlocked</string>
<!-- Browser History Screen -->
<string name="hypr_history_browser_title_1">Personal Account</string>
<string name="hypr_history_browser_title_2">Login History</string>
<string name="hypr_history_browser_no_history">No login history</string>
<!-- Workstation History Screen -->
<string name="hypr_history_workstation_title_2">Unlock History</string>
<string name="hypr_history_workstation_no_history">No unlock history</string>
<!-- Workstation Edit Screen -->
<string name="hypr_edit_workstation_title">Edit Workstation Details</string>
<string name="hypr_edit_workstation_name_hint">New Workstation Name</string>
<string name="hypr_edit_workstation_default">This is my Default Workstation</string>
<!-- Workstation Delete Screen -->
<string name="hypr_delete_workstation_title">Deleting Workstation</string>
<string name="hypr_delete_workstation_warning">WARNING</string>
<string name="hypr_delete_workstation_text_1">You are about to delete a workstation that is paired with your mobile device.</string>
<string name="hypr_delete_workstation_text_2">Please don’t forget to remove this mobile device from this workstation.</string>
<string name="hypr_delete_workstation_question">Are you sure?</string>
<!-- Workstation Delete Multiple Screen -->
<string name="hypr_delete_multiple_workstations_title">Deleting Workstations</string>
<string name="hypr_delete_multiple_workstations_text_1">You are about to delete %1$d workstations that are paired with your mobile device.</string>
<string name="hypr_delete_multiple_workstations_text_2">Please don’t forget to remove this mobile device from the following workstations.</string>
<!-- Workstation Manage Screen -->
<string name="hypr_manage_workstations_default">default</string>
<string name="hypr_manage_workstations_title">Manage Workstations</string>
<string name="hypr_manage_workstations_none">You do not have any paired workstations.</string>
<!-- Step-Up Authentication Screens -->
<string name="hypr_transaction_step_up">Step-Up Authentication Required</string>
<string name="hypr_transaction_higher_level">This transaction requires a higher level of security</string>
<string name="hypr_transaction_amount_text">$1000.00</string>
<string name="hypr_transaction_amount">$5,750.00</string>
<string name="hypr_transaction_success_result">Bank Wire Complete</string>
<string name="hypr_transaction_generic_error">Something went wrong…</string>
<!-- PIN Screens -->
<string name="hypr_pin_entry_text">Please enter your 6-digit pin to approve authentication.</string>
<string name="hypr_pin_entry_transaction_text">Please enter your 6-digit pin to approve step-up authentication.</string>
<string name="hypr_pin_transaction_error">PIN Authentication Failed. Please Try Again.</string>
<!-- Finger Screens -->
<string name="hypr_transaction_finger_instructions">Please Authenticate to approve step-up authentication.</string>
<string name="hypr_transaction_fingerprint_error">Fingerprint Authentication Failed. Please Try Again.</string>
<!-- Face Screens -->
<string name="hypr_transaction_face_error">Face Authentication Failed. Please Try Again.</string>
<!-- Settings Screen -->
<string name="hypr_info_title">TRUST ANYONE</string>
<string name="hypr_info_text_1">"HYPR is the leading provider of decentralized authentication with millions of password-less users secured across the Fortune 500. Named a “Cool Vendor” by Gartner, HYPR is trusted by major enterprises such as Mastercard and Samsung to eliminate fraud, enhance user experience, and minimize the risk of a breach."</string>
<string name="hypr_info_text_2">"Companies often store user credentials in a centralized database targeted by hackers. Centralized passwords create a single point of failure and have remained the #1 cause of mass breaches and credential reuse – until now."</string>
<string name="hypr_info_text_3">"The HYPR solution ensures that your users’ credentials always remain safe on personal devices. Eliminating centralized passwords enables HYPR to remove the target and provide a secure password-less experience for your customers and employees."</string>
<string name="hypr_info_text_4">"By decentralizing user authentication, HYPR minimizes the risk of a breach, eliminates credential reuse, and enables enterprises to <b>Trust Anyone</b>."</string>
<string name="hypr_info_email">[email protected]</string>
<!-- Reset App -->
<string name="hypr_reset_app">Reset Application</string>
<string name="hypr_reset_app_workstation">Reset App</string>
<string name="hypr_reset_app_text">Are you sure you want to reset the application?</string>
<!-- Reset App -->
<string name="hypr_contact_support_email">[email protected]</string>
<string name="hypr_contact_support_subject">Questions? Feedback? We\'ll be happy to hear from you!</string>
<string name="hypr_contact_support_email_title">Send HYPR an email…</string>
<string name="hypr_contact_support_email_body_top">-= PLEASE DO NOT TYPE BELOW THIS LINE =-</string>
<string name="hypr_contact_support_email_body_app_id">App Id: </string>
<string name="hypr_contact_support_email_body_app_version">App Version: </string>
<string name="hypr_contact_support_email_body_os">OS: </string>
<string name="hypr_contact_support_email_body_api">API: </string>
<string name="hypr_contact_support_email_body_manufacturer">Manufacturer: </string>
<string name="hypr_contact_support_email_body_model">Model: </string>
<string name="hypr_contact_support_email_body_rp_url">RP URL: </string>
<string name="hypr_contact_support_email_body_rp_app">RP App: </string>
<string name="hypr_contact_support_email_body_identifier">Identifier: </string>
Updated 2 months ago