Configuration

Add the SDK to your project

If you are using Gradle you can add the library with the following dependency.

Gradle dependency

compile 'com.onegini.mobile.sdk.android:onegini-sdk:VERSION@aar' {
    transitive = true
}

Proguard configuration

In case Proguard is used in the build process of the Android application, SDK specific configuration must be provided. Proguard processing is required in case application can't use multi-dex approach and limit of 65536 methods limit applies. Proguard configuration is attached proguard-onegini.pro.

Instantiating the SDK

All functions of the Onegini Android SDK are available from a single facade - the OneginiClient. The OneginiClient is a singleton which has to be set up once with the OneginiClientBuilder class. It can can be fetched via a OneginiClient.getInstance() method afterwards.

Note: the Application (not Activity) context must be used to instantiate the OneginiClient.

Code example OneginiClient instantiation

public class OneginiSDK {

  public static OneginiClient getOneginiClient(final Context context) {
    OneginiClient oneginiClient = OneginiClient.getInstance();
    if (oneginiClient == null) {
      oneginiClient = buildSDK(context);
    }
    return oneginiClient;
  }

  private static OneginiClient buildSDK(final Context context) {
    final Context applicationContext = context.getApplicationContext();
    final CreatePinRequestHandler createPinRequestHandler = new CreatePinRequestHandler(applicationContext);
    final PinAuthenticationRequestHandler pinAuthenticationRequestHandler = new PinAuthenticationRequestHandler(applicationContext);

    // will throw OneginiConfigNotFoundException if OneginiConfigModel class can't be found
    return new OneginiClientBuilder(applicationContext, createPinRequestHandler, pinAuthenticationRequestHandler)
        .build();
  }
}

First method that has to be called on OneginiClient after app's startup is OneginiClient#start(). This method is responsible for asynchronous initialization of the SDK. During this process the SDK can return errors, for example if the device is using deprecated android version or when the app version is outdated. Because of that the method expects a OneginiInitializationHandler in order to return initialization results.

Code example OneginiClient initialization


  // method called during app's startup
  private void setupOneginiSDK() {
    final OneginiClient oneginiClient = OneginiSDK.getOneginiClient(this);
    oneginiClient.start(new OneginiInitializationHandler() {
      @Override
      public void onSuccess(final Set<UserProfile> removedUserProfiles) {
        // remove UserProfiles that were removed on the server side and proceed with you application flow
      }

      @Override
      public void onError(final OneginiInitializationError error) {
        // handle different error types 
      }
    });
  }

Configuration model

Configuration properties should be provided to the SDK via an implementation of the OneginiClientConfigModel interface. The implementation can be provided in two ways:

  • If the SDK Configurator script was used to apply configuration and certificate pinning to your app project, the OneginiClientConfigModel implementation will be added to the app's main directory. In such case the SDK will use reflection and the Context#getPackageName() method to search for a class called {your.application.package}.OneginiConfigModel, that implements the OneginiClientConfigModel interface. If the proper class can't be found, the SDK will throw an OneginiConfigNotFoundException exception.
  • If you want to create the implementation manually, or when you want to move the generated implementation to a different (sub)package, then you can explicitly provide the config using the OneginiClientBuilder#setConfigModel() method.

Code example OneginiClientConfigModel implementation

  // ...

  final OneginiClientConfigModel configModel = new OneginiClientConfigModel() {
    /**
     * Application identifier, as configured on the Token Server.
     * Please note that once set, the id should not change. Otherwise the SDK will not be able to resolve internally stored data.
     */ 
    @Override
    public String getAppIdentifier() {
      return "myAppId";
    }

    /**
     * Application platform, as set within application version on the Token Server.
     * Please note that once set, the platform should not change. Otherwise the SDK will not be able to resolve internally stored data.
     */
    @Override
    public String getAppPlatform() {
      return "android";
    }

    /**
     * Redirect uri used in the callback from the authentication process to the client
     * @return the redirect uri used in the callback
     */
    @Override
    public String getRedirectUri() {
      return "oneginisdk://loginsuccess";
    }

    /**
     * Application version, as set within application version on the Token Server.
     */
    @Override
    public String getAppVersion() {
      return "1.0";
    }

    /**
     * Token Server's base url.
     */
    @Override
    public String getBaseUrl() {
      return "https://acme.token-server.com";
    }

    /**
     * Resource server base url.
     */ 
    @Override
    public String getResourceBaseUrl() {
      return "https://acme.resource-gateway.com";
    }

    /**
     * Resource containing certificate-to-pin identifier, by default it's `keystore.bks` located in `res/raw`.
     */
    @Override
    public int getCertificatePinningKeyStore() {
      return R.raw.keystore;
    }

    /**
     * Certificate-to-pin (`keystore.bks`) hash.
     */
    @Override
    public String getKeyStoreHash() {
      return "52b7a8bb2a72356d271a6df6bdfc88f4d78e46913adca54da77a70f260e12af3";
    }

    /**
     * Defice name. Will be used during client registration process, allows user to distinguish his devices more precisely.
     * Please note that once set, the device name should not change. Otherwise the SDK will not be able to resolve internally stored data.
     */
    @Override
    public String getDeviceName() {
      return android.os.Build.MODEL;
    }
  }

  final OneginiClient oneginiClient = new OneginiClientBuilder(applicationContext, createPinRequestHandler, pinAuthenticationRequestHandler)
      .setConfigModel(configModel)
      .build();

  // ...

It is up to the app developer to choose the storage method for the configuration parameters.

Certificate pinning

The SDK provides functionality to pin your servers certificate. Please note, if you pin the servers certificate itself you will need to deploy a new version of the application when you change the servers certificate. Best alternative is to use the intermediate certificate of the Certificate Authority used to get the server certificate (the second level in the certificate chain). This gives you the option to renew the server certificate without having to deploy a new version of the application.

Export the certificate

You can use Firefox to export the certificate. Click on the lock of the SSL website. Choose: more information. In the security tab press View certificate. Then go to the details tab. And there you can choose which certificate in the chain you wish to export.

Creating a keystore

In order to create a keystore you need a java jdk installed to get "keytool". And for android you need bouncy castle 1.46 (newer versions of bouncy castle create a keystore with a different header which is not accepted by android).

http://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on/1.46

Create keystore

keytool -import -alias MYALIAS -file /PATH/TO/CERTIFICATE/FROM/EXPORT -keystore /PATH/TO/keystore.bks -storepass PASSWORD -providerpath /PATH/TO/BOUNCYCASTLE/1.46/bcprov-ext-jdk15on-1.46.jar -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider

Add the keystore to the project

The keystore has to be added to the "raw" directory of your android project. And your configuration needs to provide the keystore within the Configuration. For example:

Keystore configuration

@Override
public int getCertificatePinningKeyStore() {
  return R.raw.keystore;
}

Keystore tampering protection

As the keystore is stored on the file system of the device, theoretically it is possible to tamper it. This is possible if the device is rooted an some malicious APP is installed. To prevent a tampered keystore, a SHA 256 hash of the keystore has to be provided by the APP. It is recommended to have the hash hardcoded and not in a separate property file or any other file on the file system. The keystore normally already is an element of the APPs binary and in that way hard coding the hash doesn't introduce any new inflexibilities.

Calculating the keystore hash

shasum -a 256 keystore.bks

Example code of hash in config model

@Override
public String getKeyStoreHash() {
  return "7A8E3D2333A1324229712B288950E317CE5BE5F956C196CEF33B46993D371575";
}

Providing your own authentication request handlers and callbacks

When the Token Server returns oAuth 2.0 refresh tokens the user needs to provide a PIN or a fingerprint to store those credentials. For this you need to provide your own authentication request handlers using OneginiClientBuilder methods. The OneginiRegistrationRequestHandler, OneginiCreatePinRequestHandler and OneginiPinAuthenticationRequestHandler are required in order to perform basic registration and authentication with PIN. Other handlers are optional and should be provided only when the app uses functionalities like fingerprint or mobile authentication. Please read authentication request handlers chapter for more details.

Other OneginiClient settings

OneginiClientBuilder exposes additional methods to customize OneginiClient behaviour:

  • shouldStoreCookies(final boolean shouldStoreCookies) method can be used to specify if http client should store cookies between requests. The default value is TRUE.
  • setHttpConnectTimeout(final int timeout) method can be used to specify http client's connection timeout (in milliseconds). Default value is 60000 (60s).
  • setHttpReadTimeout(final int timeout) method can be used to specify http client's read timeout (in milliseconds). Default value is 60000 (60s).
  • setDeviceConfigCacheDurationSeconds(final long cacheDurationSeconds) method can be used to specify device configuration cache duration (in seconds). Default value is 300s (5 minutes). Passing 0 will disable the cache.

Sample initialization call

  public static OneginiClient getOneginiClient(final Context context) {
    OneginiClient oneginiClient = OneginiClient.getInstance();
    if (oneginiClient == null) {
      final Context applicationContext = context.getApplicationContext();
      final RegistrationRequestHandler registrationRequestHandler = new RegistrationRequestHandler(applicationContext);
      final OneginiCreatePinRequestHandler createPinRequestHandler = new CreatePinRequestHandler();
      final OneginiPinAuthenticationRequestHandler pinAuthenticationRequestHandler = new PinAuthenticationRequestHandler();
      final OneginiFingerprintAuthenticationRequestHandler fingerprintAuthenticationRequestHandler = new FingerprintAuthenticationRequesthandler();

      // will throw OneginiConfigNotFoundException if OneginiConfigModel class can't be found
      oneginiClient = new OneginiClientBuilder(applicationContext, registrationRequestHandler, createPinRequestHandler, pinAuthenticationRequestHandler)
          // optional settings
          .setFingerprintAuthenticatioRequestHandler(fingerprintAuthenticationRequestHandler)
          .setConfigModel(new OneginiConfigModel())
          .setHttpReadTimeout(30000)
          .setDeviceConfigCacheDurationSeconds(600)
          .build();
    }
    return oneginiClient;
  }