Commit 03624928 authored by Claudio Marforio's avatar Claudio Marforio
Browse files

Version 1.0.0

- Maven integration (and update to docs)
- Proguard rules sample
- Update sample app
- Update version of futuraekit.aar to 1.0.0
parent 85fa5584
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
buildToolsVersion '28.0.3'
compileSdkVersion 29
buildToolsVersion '29.0.2'
defaultConfig {
applicationId "com.futurae.futuraedemo"
......@@ -14,17 +14,28 @@ android {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled true
proguardFile 'proguard/futurae.pro'
proguardFile getDefaultProguardFile('proguard-android.txt')
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors {
}
repositories {
maven {
url "https://artifactory.futurae.com/artifactory/futurae-mobile"
credentials {
username = "anonymous"
password = ""
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
......@@ -37,12 +48,7 @@ dependencies {
implementation 'com.google.firebase:firebase-core:16.0.7'
implementation 'com.google.firebase:firebase-messaging:17.4.0'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
implementation 'com.squareup.moshi:moshi-adapters:1.6.0'
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
implementation(name:'futuraekit', ext:'aar')
implementation('com.futurae.sdk:futuraekit:1.0.0')
}
apply plugin: 'com.google.gms.google-services'
-keep public enum com.futurae.sdk.model.Account$** {
**[] $VALUES;
public *;
}
-keep class com.futurae.sdk.DeviceInfo {
*;
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.futurae.futuraedemo">
package="com.futurae.futuraedemo">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
......@@ -11,13 +12,13 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name="com.futurae.futuraedemo.AppMain"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name="com.futurae.futuraedemo.AppMain"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="com.google.android.gms.vision.DEPENDENCIES"
......@@ -47,9 +48,9 @@
</activity>
<activity
android:exported="false"
android:name=".ui.FTRQRCodeActivity"
android:theme="@style/AppTheme" />
android:exported="false"
android:name=".ui.FTRQRCodeActivity"
android:theme="@style/AppTheme" />
</application>
</manifest>
\ No newline at end of file
......@@ -2,6 +2,7 @@ package com.futurae.futuraedemo;
import android.app.Application;
import com.futurae.sdk.FuturaeClient;
import com.futurae.sdk.Kit;
public class AppMain extends Application {
......@@ -11,6 +12,6 @@ public class AppMain extends Application {
super.onCreate();
FuturaeClient.launch(this, null);
FuturaeClient.launch(this, (Kit)null);
}
}
......@@ -21,9 +21,12 @@ import android.util.Log;
import com.futurae.futuraedemo.R;
import com.futurae.sdk.FuturaeCallback;
import com.futurae.sdk.FuturaeClient;
import com.futurae.sdk.FuturaeResultCallback;
import com.futurae.sdk.approve.ApproveSession;
import com.futurae.sdk.model.Account;
import com.futurae.sdk.model.ApproveInfo;
import com.futurae.sdk.model.CurrentTotp;
import com.futurae.sdk.model.SessionInfo;
import com.futurae.sdk.utils.NotificationUtils;
import com.google.android.gms.vision.barcode.Barcode;
......@@ -69,27 +72,46 @@ public class MainActivity extends AppCompatActivity {
// TODO: Handle URI call
final String uriCall = getIntent().getDataString();
if (!TextUtils.isEmpty(uriCall)) {
FuturaeClient.sharedClient().handleUri(uriCall, new FuturaeCallback() {
@Override
public void success() {
Log.i(TAG, "success: URI handled");
// TODO: Handle enrollment and MobileAuth
if (uriCall.contains("enroll")) {
// URI Enrollment
// Enrollment URI
if (uriCall.contains("enroll")) {
FuturaeClient.sharedClient().handleUri(uriCall, new FuturaeCallback() {
@Override
public void success() {
Log.i(TAG, "success: URI handled");
showAlert("Success", "Successfully enrolled");
} else {
// MobileAuth
finish();
}
}
@Override
public void failure(Throwable throwable) {
Log.e(TAG, "failure: failed to handle URI: " + throwable);
showAlert("Error", "Could not handle URI call");
}
});
@Override
public void failure(Throwable throwable) {
Log.e(TAG, "failure: failed to handle URI: " + throwable);
showAlert("Error", "Could not handle URI call");
}
});
return;
}
// Auth URI
if (uriCall.contains("auth")) {
String userId = FuturaeClient.getUserIdFromUri(uriCall);
String sessionToken = FuturaeClient.getSessionTokenFromUri(uriCall);
FuturaeClient.sharedClient().sessionInfoByToken(userId, sessionToken,
new FuturaeResultCallback<SessionInfo>() {
@Override
public void success(SessionInfo sessionInfo) {
showApproveAlertDialog(new ApproveSession(sessionInfo), true);
}
@Override
public void failure(Throwable t) {
Log.e(TAG, "QR Code authentication failed: " + t.getLocalizedMessage());
}
});
return;
}
Log.e(TAG, "failure: failed to handle URI: " + uriCall);
showAlert("Error", "Could not handle URI call");
}
}
......@@ -145,7 +167,7 @@ public class MainActivity extends AppCompatActivity {
final Account account = accounts.get(0);
CurrentTotp totp = FuturaeClient.sharedClient().nextTotp(account.getUserId());
showAlert("TOTP", "Code: " + totp.passcode + "\nRemaining seconds: " + totp.remainingSecs);
showAlert("TOTP", "Code: " + totp.getPasscode() + "\nRemaining seconds: " + totp.getRemainingSecs());
}
// QRCode callbacks
......@@ -155,48 +177,55 @@ public class MainActivity extends AppCompatActivity {
Barcode qrcode = data.getParcelableExtra(FTRQRCodeActivity.PARAM_BARCODE);
Log.i(TAG, "Scanned activation code from the QR code; will enroll device");
FuturaeClient.sharedClient().enroll(qrcode.rawValue,
new FuturaeCallback() {
@Override
public void success() {
Log.i(TAG, "Enrollment successful");
showAlert("Success", "Enrollment successful");
}
new FuturaeCallback() {
@Override
public void success() {
Log.i(TAG, "Enrollment successful");
showAlert("Success", "Enrollment successful");
}
@Override
public void failure(Throwable throwable) {
Log.e(TAG, "Enrollment failed: " + throwable.getLocalizedMessage());
showAlert("Error", "Enrollment failed");
}
});
@Override
public void failure(Throwable throwable) {
Log.e(TAG, "Enrollment failed: " + throwable.getLocalizedMessage());
showAlert("Error", "Enrollment failed");
}
});
}
private void onAuthQRCodeScanned(Intent data) {
// TODO: Handle QRCode auth response
Barcode qrcode = data.getParcelableExtra(FTRQRCodeActivity.PARAM_BARCODE);
Log.i(TAG, "Scanned authentication data from the QR code; will reply to server");
FuturaeClient.sharedClient().approveAuth(qrcode.rawValue,
new FuturaeCallback() {
@Override
public void success() {
Log.i(TAG, "QR Code authentication succeeded");
}
String userId = FuturaeClient.getUserIdFromQrcode(qrcode.rawValue);
String sessionToken = FuturaeClient.getSessionTokenFromQrcode(qrcode.rawValue);
@Override
public void failure(Throwable throwable) {
Log.e(TAG, "QR Code authentication failed: "
+ throwable.getLocalizedMessage());
}
});
FuturaeClient.sharedClient().sessionInfoByToken(userId, sessionToken,
new FuturaeResultCallback<SessionInfo>() {
@Override
public void success(SessionInfo sessionInfo) {
showApproveAlertDialog(new ApproveSession(sessionInfo), false);
}
@Override
public void failure(Throwable t) {
Log.e(TAG, "QR Code authentication failed: " + t.getLocalizedMessage());
}
});
}
// Approve dialog
void showApproveAlertDialog(final ApproveSession session) {
void showApproveAlertDialog(final ApproveSession session, final boolean isFromUri) {
// TODO: For demo purposes we simply show an alert instead of an approve screen
StringBuffer sb = new StringBuffer();
if (session.getInfo() != null) {
sb.append("\n");
for (ApproveInfo info : session.getInfo()) {
sb.append(info.getKey()).append(": ").append(info.getValue()).append("\n");
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Approve");
builder.setMessage("Would you like to approve the request?");
builder.setMessage("Would you like to approve the request?" + sb.toString());
builder.setPositiveButton("Approve", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
FuturaeClient.sharedClient().approveAuth(session.getUserId(),
......@@ -204,13 +233,16 @@ public class MainActivity extends AppCompatActivity {
@Override
public void success() {
Log.i(TAG, "Approve session allowed");
if (isFromUri) {
finish();
}
}
@Override
public void failure(Throwable t) {
Log.e(TAG, "Failed to approve session: " + t.getLocalizedMessage());
}
});
}, session.getInfo());
}
});
builder.setNegativeButton("Deny", new DialogInterface.OnClickListener() {
......@@ -220,13 +252,16 @@ public class MainActivity extends AppCompatActivity {
@Override
public void success() {
Log.i(TAG, "Approve session rejected");
if (isFromUri) {
finish();
}
}
@Override
public void failure(Throwable t) {
Log.e(TAG, "Failed to approve session: " + t.getLocalizedMessage());
}
});
}, session.getInfo());
}
});
......@@ -303,17 +338,21 @@ public class MainActivity extends AppCompatActivity {
final ApproveSession session = intent.getParcelableExtra(
NotificationUtils.PARAM_APPROVE_SESSION);
if (approveDialog != null && approveDialog.isShowing()) {
approveDialog.dismiss();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
showApproveAlertDialog(session);
showApproveAlertDialog(session, false);
}
});
break;
case NotificationUtils.INTENT_APPROVE_CANCEL_MESSAGE:
if (approveDialog != null && approveDialog.isShowing()) {
approveDialog.dismiss();
approveDialog.dismiss();
}
break;
......
......@@ -13,6 +13,6 @@
<!--ERRORS-->
<string name="error_low_storage">QR code scanning cannot be performed due to low device storage</string>
<string name="main_btn_logout">Logout</string>
<string name="main_btn_logout">Logout</string>
</resources>
......@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.android.tools.build:gradle:4.0.0'
classpath 'com.google.gms:google-services:4.2.0'
}
}
......@@ -14,9 +14,6 @@ allprojects {
repositories {
google()
jcenter()
flatDir {
dirs 'src/main/libs'
}
}
}
......
#Fri Mar 15 11:07:02 CET 2019
#Wed Jul 22 17:41:42 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
......@@ -5,11 +5,12 @@ This is the Android SDK of Futurae. You can read more about Futurae™ at [futur
## Table of contents
* [Basic integration](#basic-integration)
* [Get FuturaeKit SDK for Android](#get-futuraekit-sdk-for-android)
* [Add SDK to Project](#add-sdk-to-project)
* [Get FuturaeKit SDK for Android via Maven](#get-futuraekit-sdk-for-android-via-maven)
* [Get FuturaeKit SDK for Android Manually](#get-futuraekit-sdk-for-android-manually)
* [Add permissions](#add-permissions)
* [Basic setup](#basic-setup)
* [Build your app](#build-your-app)
* [R8 / Proguard](#r8-proguard)
* [Features](#features)
* [Callbacks](#callbacks)
* [URI Schemes](#uri-schemes)
......@@ -32,21 +33,43 @@ This is the Android SDK of Futurae. You can read more about Futurae™ at [futur
## <a id="basic-integration" />Basic integration
We will describe the steps to integrate the FuturaeKit SDK into your Android project. We are going to assume that you are using Android Studio for your development.
### <a id="get-futuraekit-sdk-for-android" />Get FuturaeKit SDK for Android
You can download the latest SDK from the [releases](https://git.futurae.com/futurae-public/futurae-android-sdk/tags), or clone this repository directly.
This repository also contains a simple demo app to show how the SDK can be integrated.
### <a id="get-futuraekit-sdk-for-android-via-maven" />Get FuturaeKit SDK for Android via Maven
The *preferred* way to get the FuturaeKit SDK fro Android is through maven and the Futurae private repository.
To do so add the following lines to your `build.gradle` file:
```
repositories {
maven {
url "https://artifactory.futurae.com/artifactory/futurae-mobile"
credentials {
username = "anonymous"
password = ""
}
}
}
dependencies {
implementation('com.futurae.sdk:futuraekit:1.0.0')
}
```
Of course, make sure to specify the correct version number.
### <a id="get-futuraekit-sdk-for-android-via-maven" />Get FuturaeKit SDK for Android Manually
Alternatively, *although discouraged*, you can download the latest SDK from the [releases](https://git.futurae.com/futurae-public/futurae-android-sdk/tags), or clone this repository directly.
This repository also contains a simple demo app to show how the SDK can be integrated.
### <a id="add-sdk-to-project" />Add SDK to Project
To integrate the FuturaeKit SDK into your project, copy `futuraekit.aar` into the `src/main/libs` folder of your app.
Then, in your modules `build.gradle` (the one under "app"), add the following dependencies:
```
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-moshi:2.3.0'
compile 'com.squareup.moshi:moshi-adapters:1.4.0'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.3.0'
implementation 'com.squareup.moshi:moshi-adapters:1.4.0'
implementation 'com.squareup.okhttp3:okhttp:3.8.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.8.0'
compile(name:'futuraekit', ext:'aar')
implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
implementation 'com.github.nisrulz:easydeviceinfo-base:2.4.1'
implementation files('src/main/libs/futuraekit.aar')
```
![][gradle-app]
......@@ -124,6 +147,12 @@ Secondly, in your `res/values` folder make sure to create a file `futurae.xml` w
Build and run your app. If the build succeeds, you should carefully read the SDK logs in the console.
### <a id="r8-proguard" />R8 / ProGuard
If you are using R8 or ProGuard add the options from [build.gradle][build_gradle].
## <a id="features" />Features
### <a id="callbacks" />Callbacks
The SDK methods that perform API calls use callbacks as the feedback mechanism. These calls expect an object of the `FuturaeCallback` interface as an argument:
......@@ -152,6 +181,8 @@ FuturaeClient.sharedClient().handleUri(uriString, new FuturaeCallback() {
});
```
**Note**: In case you attempt an authentication with a URI scheme call, and this authentication includes extra information to be displayed to the user, you must retrieve this information from the server and include it in the authentication response; see section [Session Information](#session-information) for details on how to do that.
### <a id="push-notifications" />Push Notifications
Your app must be set up to receive Firebase Cloud Messaging (FCM) push notifications from our server. You can choose to receive and handle these notifications yourself, or alternatively you can use the existing infrastructure provided in the SDK. You can find more information on how to setup FCM push notifications for your app in the [Firebase Cloud Messaging Developer Guide](https://firebase.google.com/docs/cloud-messaging/).
......@@ -348,6 +379,8 @@ FuturaeClient.sharedClient().approveAuth(qrCodeString, new FuturaeCallback() {
});
```
**Note**: In case you attempt an authentication with a QR Code, and this authentication includes extra information to be displayed to the user, you must retrieve this information from the server and include it in the authentication response; see section [Session Information](#session-information) for details on how to do that.
#### <a id="approve-auth" />Push Notification Factor
When a Push Notification Factor session is initiated on the server side, the server will send a push notification to the app, where the user should approve or reject the authentication session. The notification is received and handled by the SDK, which in turn will send a local broadcast (see `INTENT_APPROVE_AUTH_MESSAGE` [above](#local-intents)), so that the app can perform any required actions. For example, the app might want to display a prompt to the user so that they can approve or reject the session.
......@@ -360,6 +393,8 @@ String sessionId = authSession.getSessionId();
Once the outcome of the authentication has been decided by the app, it should be sent to the server for the authentication session to complete.
**Note**: In case you attempt an authentication via push notification, and this authentication includes extra information to be displayed to the user, you must retrieve this information from the server and include it in the authentication response; see section [Session Information](#session-information) for details on how to do that.
##### <a id="approve-reply" />Approve Authentication
To approve the authentication session, use the following method:
```java
......@@ -397,8 +432,8 @@ FuturaeClient.sharedClient().rejectAuth(userId, sessionId, reportFraud, new Futu
The TOTP Factor can be used for offline authentication, as there is no requirement for an internet connection in the app. To get the current TOTP generated by the SDK for a specific user account, call the following method:
```java
CurrentTotp totp = FuturaeClient.sharedClient().nextTotp(userId);
String passcode = totp.passcode; // The TOTP that the user should use to authenticate
int remainingSeconds = totp.remainingSecs; // The remaining seconds of validity of this TOTP
String passcode = totp.getPasscode(); // The TOTP that the user should use to authenticate
int remainingSeconds = totp.getRemainingSecs(); // The remaining seconds of validity of this TOTP
```
As seen in this example, the `nextTotp()` method returns an object that contains the TOTP itself, but also the remaining seconds that this TOTP will be still valid for. After this time, a new TOTP must be obtained by the SDK.
......@@ -436,6 +471,39 @@ FuturaeClient.sharedClient().sessionInfoByToken(userId, sessionId, new FuturaeRe
If there is extra information to be displayed to the user, for example when confirming a transaction, this will be indicated with a list of key-value pairs in the `extra_info` part of the response.
In order to query the server for the session information, you need the user ID and the session ID or token. You can use the following helper methods to obtain these from either a URI or a QR Code:
```java
public class FuturaeClient {
public static String getUserIdFromQrcode(String qrCode);
public static String getSessionTokenFromQrcode(String qrCode);
public static String getUserIdFromUri(String uri);
public static String getSessionTokenFromUri(String uri);
}
```
##### <a id="auth-extra-info" />Authentication with extra information
In case it exists, you can retrieve the extra information that the user needs to review while authenticating by calling the following method on the `SessionInfo` object:
```java
public ApproveInfo[] getApproveInfo();
```
Each `ApproveInfo` object is a key-value pair that describes a single piece of information; call the following methods to retrieve the key and value of the object respectively.
```java
public String getKey();
public String getValue();
```
**IMPORTANT**: In case the authentication contains such extra information, it is mandatory that the information is fetched from the server, displayed to the user, and included in the authentication response in order for it to be included in the response signature; otherwise the server will reject the authentication response. Use the following methods to send an authentication response including the authentication information:
```java
class FuturaeClient {
public void approveAuth(String qrCode, final FuturaeCallback callback, ApproveInfo[] extraInfo);
public void approveAuth(String userId, String sessionId, final FuturaeCallback callback, ApproveInfo[] extraInfo);
public void rejectAuth(String userId, String sessionId, boolean reportFraud, final FuturaeCallback callback, ApproveInfo[] extraInfo);
}
```
[futurae.com]: http://www.futurae.com
......@@ -444,3 +512,4 @@ If there is extra information to be displayed to the user, for example when conf
[config-xml]: ./Resources/config-xml.png
[android_application]: http://developer.android.com/reference/android/app/Application.html
[build_gradle]: ./app/build.gradle
No preview for this file type
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment