Technical Articles
Step by Step with the SAP Cloud Platform SDK for Android — Part 7 — Remembering Credentials using the Secure Store
Previous (Offline) Home 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.
- Add a new class named BasicAuthPersistentStore and replace its contents with the code shown below.
package com.sap.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; } }
- Add the below variable in MainActivity
private BasicAuthPersistentStore bapStore;
- 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.
- 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))
- 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.
- Optionally add an unregister button by adding the following XML to res/layout/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(); }
- 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);
- 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.
- Add the following global declaration to the MainActivity.
private long startTime;
- Add the following code to the start of the onCreate method, just below the super.onCreate call, to get the startTime of the app.
startTime = System.currentTimeMillis();
- 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.");
- Add the following variable declaration to the start of the setupOfflineOData method.
long startOfOpenOfflineStore = System.currentTimeMillis();
- Add the following code to the setOfflineOData method in 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.");
- Run the app and notice in Android Studio’s Logcat view that the timings are now shown.
Previous (Offline) Home Next (Client Usage)