Skip to Content

Step by Step with the SAP Cloud Platform SDK for Android

Part 7 — Remembering Credentials using the Secure Store

Previous (Offline)   Next (Client Usage)

The SDK provides an encrypted storage which is a good place to store credentials.

The following are some additional sources of documentation on the secure store.
Secure Store
Secure Store API Reference

The below steps will create a secure store and use it with the BasicAuthDialogAuthenticator so that entered passwords are remembered between application restarts.

  1. Add a new class named BasicAuthPersistentStore and replace its contents with the code shown below.
    package com.example.android.stepbystep;
    
    import com.sap.cloud.mobile.foundation.authentication.BasicAuthCredentialStore;
    import com.sap.cloud.mobile.foundation.securestore.SecureKeyValueStore;
    
    public class BasicAuthPersistentStore implements BasicAuthCredentialStore {
        SecureKeyValueStore mSecureStore;
        final String KEY_PREFIX = "basic auth key prefix";
    
        public BasicAuthPersistentStore() {}
    
        public BasicAuthPersistentStore(SecureKeyValueStore secureKeyValueStore) {
            mSecureStore = secureKeyValueStore;
        }
    
        @Override
        public void storeCredential(String host, String realm, String[] credentials) {
            if (mSecureStore != null) {
                mSecureStore.put(key(host, realm), credentials);
            }
        }
    
        @Override
        public String[] getCredential(String host, String realm) {
            if (mSecureStore != null) {
                return mSecureStore.getSerializable(key(host, realm));
            }
            return null;
        }
    
        @Override
        public void deleteCredential(String host, String realm) {
            if (mSecureStore != null) {
                mSecureStore.remove(key(host, realm));
            }
        }
    
        @Override
        public void deleteAllCredentials() {
            if (mSecureStore != null) {
                String[] keys = mSecureStore.keys();
                for (String key:keys) {
                    if (key.startsWith(KEY_PREFIX)) {
                        mSecureStore.remove(key);
                    }
                }
            }
        }
    
        public void setStore(SecureKeyValueStore secureKeyValueStore) {
            mSecureStore = secureKeyValueStore;
        }
    
        private String key(String host, String realm) {
            return KEY_PREFIX + host + ":" + realm;
        }
    }
    
  2. Add the below variable in MainActivity
    private BasicAuthPersistentStore bapStore;
    
  3. Add the below code near the end of the onCreate method but before the call to onRegister(null).
    SecureKeyValueStore myStore = new SecureKeyValueStore(this.getApplicationContext(), "mySecureStore");
    try {
        myStore.open(EncryptionUtil.getEncryptionKey("myAlias"));  //For additional security, consider using a passcode screen.
        bapStore = new BasicAuthPersistentStore(myStore);
    }
    catch (OpenFailureException e) {
        e.printStackTrace();
    }
    catch (EncryptionError encryptionError) {
        encryptionError.printStackTrace();
    }

    See also Encryption Utility.

  4. In the onRegister method, comment out and replace the existing line of code to pass the secure store to the BasicAuthDialogAuthenticator.
    //.authenticator(new BasicAuthDialogAuthenticator())
    .authenticator(new BasicAuthDialogAuthenticator(bapStore))
  5. Deploy and run the app. Remove the app from memory and restart the app.

    Notice that when it is restarted, it no longer asks for a user name and password as the saved credentials are automatically provided when an authentication challenge is provided.

    Note, on the emulator with instant run enabled, it seems like the app was not asking for credentials on restart. To better see the change, try this on a device or disable instant run.

  6. Optionally add an unregister button by adding the following xml to activity_main.xml.
    <Button
        android:id="@+id/b_unregister"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onUnregister"
        android:enabled="false"
        android:text="Unregister" />
    

    Add the following methods to MainActivity.

    private void logout() {
        Log.d(myTag, "In logout");
        Request request = new Request.Builder()
                .post(RequestBody.create(null, ""))
                .url(serviceURL + "/mobileservices/sessions/logout")
                .build();
    
        Callback updateUICallback = new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                Log.d(myTag, "onFailure called during registration " + e.getLocalizedMessage());
            }
    
            @Override
            public void onResponse(Call call, final Response response) throws IOException {
                if (response.isSuccessful()) {
                    Log.d(myTag, "Successfully logged out");
                    toastAMessage("Successfully logged out");
                    enableButtonsOnRegister(false);
                    onRegister(null);
                }
                else {
                    Log.d(myTag, "Logout failed " + response.networkResponse());
                    toastAMessage("Logout failed " + response.networkResponse());
                }
            }
        };
    
        myOkHttpClient.newCall(request).enqueue(updateUICallback);
    }
    
    public void onUnregister(final View view) {
        Log.d(myTag, "In onUnregister");
        bapStore.deleteAllCredentials();  
        logout();
    }
    
  7. Add the following line to the start of the enableButtonsOnRegister method.
    final Button unregisterButton = (Button) findViewById(R.id.b_unregister);

    Add this line to the run method of the handler in the enableButtonsOnRegister method.

    unregisterButton.setEnabled(enable);
  8. Run the app and notice that there is now an unregister button that when pressed allows the user to logout which removes the saved credentials.

Logging Startup Times

It is useful to log some metrics regarding the startup time of the app to see how different components impact it.
This can be best done now that we have implemented remembering credentials, since this removes the time of the user entering their credentials from the measurement.

  1. Add the following global declaration to the MainActivity.
    private long startTime;
    
  2. Add the following code to the start of the onCreate method to get the startTime of the app.
    startTime = System.currentTimeMillis();
    
  3. In the onRegister method, add the following code to the response.isSuccessful() block of the onResponse callback. This calculates and logs the time, in ms, taken to open the app and validate the user’s credentials.
    long registerTime = System.currentTimeMillis() - startTime;
    float registerTimeInSeconds = new Float(Math.round(registerTime/10)) / 100;
    Log.d(myTag, "Registration finished " + registerTimeInSeconds + " seconds after the application started.");
    
  4. Add the following variable declaration to the start of the setupOfflineOData method.
    long startOfOpenOfflineStore = System.currentTimeMillis();
  5. Add the following code to the success block of the myOfflineDataProvider.open call to calculate and log the time taken to setup up the offline store.
    long openOfflineStoreTime = System.currentTimeMillis() - startOfOpenOfflineStore;
    long appReadyTime = System.currentTimeMillis() - startTime;
    float openOfflineStoreTimeInSeconds = new Float(Math.round(openOfflineStoreTime/10)) / 100;
    float appReadyTimeInSeconds = new Float(Math.round(appReadyTime/10)) / 100;
    Log.d(myTag, "Offline store opened in " + openOfflineStoreTimeInSeconds + " seconds.");
    Log.d(myTag, "App is ready " + appReadyTimeInSeconds + " seconds after the application started.");
    
  6. Run the app and notice in Android Studio’s Logcat view that the timings are now shown.

Previous (Offline)   Next (Client Usage)

 

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply