Authentication¶
Basic Authentication Configuration with In-Memory Credential Store¶
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance());
AppHeadersInterceptor appHeadersInterceptor =
new AppHeadersInterceptor();
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
List<ConnectionSpec> connectionSpecs = new LinkedList<>();
ConnectionSpec strict = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build();
connectionSpecs.add(strict);
// Uses built-in memory store for storing credentials
ClientProvider.set( new OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.authenticator(new BasicAuthDialogAuthenticator())
.cookieJar(new WebkitCookieJar())
.build());
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance())
val appHeadersInterceptor = AppHeadersInterceptor()
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
val connectionSpecs = LinkedList<ConnectionSpec>()
val strict = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build()
connectionSpecs.add(strict)
// Uses built-in memory store for storing credentials
ClientProvider.set(OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.authenticator(BasicAuthDialogAuthenticator())
.cookieJar(WebkitCookieJar())
.build())
Basic Authentication Configuration with Persistent Credential Store¶
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance());
AppHeadersInterceptor appHeadersInterceptor =
new AppHeadersInterceptor();
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
List<ConnectionSpec> connectionSpecs = new LinkedList<>();
ConnectionSpec strict = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build();
connectionSpecs.add(strict);
// PersistentCredentialStore is a custom implementation of BasicAuthCredentialStore that
// uses secure store for storing credentials.
PersistentCredentialStore basicAuthPersistentStore = new PersistentCredentialStore();
ClientProvider.set( new OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.authenticator(new BasicAuthDialogAuthenticator(basicAuthPersistentStore))
.cookieJar(new WebkitCookieJar())
.build());
// Remember to close the store when application is closed
basicAuthPersistentStore.close();
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance())
val appHeadersInterceptor = AppHeadersInterceptor()
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
val connectionSpecs = LinkedList<ConnectionSpec>()
val strict = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build()
connectionSpecs.add(strict)
// PersistentCredentialStore is a custom implementation of BasicAuthCredentialStore that
// uses secure store for storing credentials.
val basicAuthPersistentStore = PersistentCredentialStore()
ClientProvider.set(OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.authenticator(BasicAuthDialogAuthenticator(basicAuthPersistentStore))
.cookieJar(WebkitCookieJar())
.build())
// Remember to close the store when application is closed
basicAuthPersistentStore.close()
SAML Authentication Configuration¶
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance());
AppHeadersInterceptor appHeadersInterceptor = new AppHeadersInterceptor();
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
List<ConnectionSpec> connectionSpecs = new LinkedList<>();
ConnectionSpec strict = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build();
connectionSpecs.add(strict);
ClientProvider.set( new OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(new SamlInterceptor())
.cookieJar(new WebkitCookieJar())
.build());
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance())
val appHeadersInterceptor = AppHeadersInterceptor()
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
val connectionSpecs = LinkedList<ConnectionSpec>()
val strict = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build()
connectionSpecs.add(strict)
ClientProvider.set(OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(SamlInterceptor())
.cookieJar(WebkitCookieJar())
.build())
SAML Authentication Configuration Using Authentication URL¶
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance());
AppHeadersInterceptor appHeadersInterceptor = new AppHeadersInterceptor();
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
List<ConnectionSpec> connectionSpecs = new LinkedList<>();
ConnectionSpec strict = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build();
connectionSpecs.add(strict);
ClientProvider.set( new OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(new SamlInterceptor(
new SamlConfiguration.Builder().authUrl(
SettingsProvider.get().getBackendUrl()
+ "/SAMLAuthLauncher").build()))
.cookieJar(new WebkitCookieJar())
.build());
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance())
val appHeadersInterceptor = AppHeadersInterceptor()
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
val connectionSpecs = LinkedList<ConnectionSpec>()
val strict = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build()
connectionSpecs.add(strict)
ClientProvider.set(OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(SamlInterceptor(
SamlConfiguration.Builder().authUrl(
SettingsProvider.get().getBackendUrl() + "/SAMLAuthLauncher").build()))
.cookieJar(WebkitCookieJar())
.build())
SAML Authentication Configuration Using WebView
¶
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance());
AppHeadersInterceptor appHeadersInterceptor = new AppHeadersInterceptor();
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
List<ConnectionSpec> connectionSpecs = new LinkedList<>();
ConnectionSpec strict = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build();
connectionSpecs.add(strict);
ClientProvider.set( new OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(new SamlInterceptor(new SamlWebViewProcessor(
new SamlConfiguration.Builder().authUrl(SettingsProvider.get().getBackendUrl()
+ "/SAMLAuthLauncher").build())))
.cookieJar(new WebkitCookieJar())
.build());
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance())
val appHeadersInterceptor = AppHeadersInterceptor()
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
val connectionSpecs = LinkedList<ConnectionSpec>()
val strict = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build()
connectionSpecs.add(strict)
ClientProvider.set(OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(SamlInterceptor(SamlWebViewProcessor(
SamlConfiguration.Builder().authUrl(SettingsProvider.get().getBackendUrl() + "/SAMLAuthLauncher").build())))
.cookieJar(WebkitCookieJar())
.build())
SAML Authentication Configuration for Certificate-Based Authentication¶
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance());
AppHeadersInterceptor appHeadersInterceptor = new AppHeadersInterceptor();
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
List<ConnectionSpec> connectionSpecs = new LinkedList<>();
ConnectionSpec strict = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build();
connectionSpecs.add(strict);
ClientProvider.set( new OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(new SamlInterceptor(new SamlWebViewProcessor(
new SamlConfiguration.Builder().authUrl(SettingsProvider.get().getBackendUrl()
+ "/SAMLAuthLauncher").build(),
//System certificate provider presents a dialog to select a certificate
new SystemCertificateProvider())))
.cookieJar(new WebkitCookieJar())
.build());
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance())
val appHeadersInterceptor = AppHeadersInterceptor()
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
val connectionSpecs = LinkedList<ConnectionSpec>()
val strict = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build()
connectionSpecs.add(strict)
ClientProvider.set(OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(SamlInterceptor(SamlWebViewProcessor(
SamlConfiguration.Builder().authUrl(SettingsProvider.get().getBackendUrl() + "/SAMLAuthLauncher").build(),
//System certificate provider presents a dialog to select a certificate
SystemCertificateProvider())))
.cookieJar(WebkitCookieJar())
.build())
OAuth Configuration for Authorization Code Flow¶
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance());
AppHeadersInterceptor appHeadersInterceptor =
new AppHeadersInterceptor();
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
List<ConnectionSpec> connectionSpecs = new LinkedList<>();
ConnectionSpec strict = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build();
connectionSpecs.add(strict);
// Use Chrome custom tabs or built-in allowlisted browsers to process oauth flow
// ClientId and Url values are displayed in SAP BTP cockpit
// (Application security page, click edit for details)
OAuth2BrowserProcessor browserProcessor = new OAuth2BrowserProcessor(
new OAuth2Configuration.Builder()
.clientId("48417b17-685d-44d7-8377-4b4030d6be33")
.authUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/authorize")
.redirectUrl("com.sap.template://oauthasservices-youraccountid.hana.ondemand.com")
.tokenUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/token")
.responseType(OAuth2Configuration.CODE_RESPONSE_TYPE)
.build());
/*
Following entry must be added to app `AndroidManifest.xml` to match the redirectUrl scheme above
<activity android:name="com.sap.cloud.mobile.foundation.authentication.OAuth2RedirectActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.sap.template" />
</intent-filter>
</activity>
*/
/*
// OR, use Webview as the browser to process oauth flow
// ClientId and Url values are displayed in SAP BTP cockpit
// (Application security page, click edit for details)
OAuth2WebViewProcessor webviewProcessor = new OAuth2WebViewProcessor(
new OAuth2Configuration.Builder()
.clientId("48417b17-685d-44d7-8377-4b4040d6be33")
.authUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/authorize")
.redirectUrl("https://oauthasservices-youraccountid.hana.ondemand.com")
.tokenUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/token")
.responseType(OAuth2Configuration.CODE_RESPONSE_TYPE)
.build());
Following entry must be added to app `AndroidManifest.xml` to match the redirectUrl scheme and host above
<activity android:name="com.sap.cloud.mobile.foundation.authentication.OAuth2RedirectActivity">
<intent-filter android:autoVerify="false">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="oauthasservices-youraccountid.hana.ondemand.com"
android:path="/networking_oauth_https_redirect/oauth2redirect" />
</intent-filter>
</activity>
*/
// PersistentTokenStore is a custom implementation of OAuth2TokenStore that
// uses secure store for storing credentials.
PersistentTokenStore persistentTokenStore = new PersistentTokenStore();
ClientProvider.set( new OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(new OAuth2Interceptor(browserProcessor,
persistentTokenStore)) //uses persistent token store
.cookieJar(new WebkitCookieJar())
.build());
// Remember to close the store when application is closed
persistentTokenStore.close();
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance())
val appHeadersInterceptor = AppHeadersInterceptor()
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
val connectionSpecs = LinkedList<ConnectionSpec>()
val strict = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build()
connectionSpecs.add(strict)
// Use Chrome custom tabs or built-in allowlisted browsers to process oauth flow
// ClientId and Url values are displayed in SAP BTP cockpit
// (Application security page, click edit for details)
val browserProcessor = OAuth2BrowserProcessor(
OAuth2Configuration.Builder()
.clientId("48417b17-685d-44d7-8377-4b4030d6be33")
.authUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/authorize")
.redirectUrl("com.sap.template://oauthasservices-youraccountid.hana.ondemand.com")
.tokenUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/token")
.responseType(OAuth2Configuration.CODE_RESPONSE_TYPE)
.build())
/*
Following entry must be added to app `AndroidManifest.xml` to match the redirectUrl scheme above
<activity android:name="com.sap.cloud.mobile.foundation.authentication.OAuth2RedirectActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.sap.template" />
</intent-filter>
</activity>
*/
/*
// OR, use Webview as the browser to process oauth flow
// ClientId and Url values are displayed in SAP BTP cockpit
// (Application security page, click edit for details)
val webviewProcessor = OAuth2WebViewProcessor(
OAuth2Configuration.Builder()
.clientId("48417b17-685d-44d7-8377-4b4040d6be33")
.authUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/authorize")
.redirectUrl("https://oauthasservices-youraccountid.hana.ondemand.com")
.tokenUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/token")
.responseType(OAuth2Configuration.CODE_RESPONSE_TYPE)
.build())
Following entry must be added to app `AndroidManifest.xml` to match the redirectUrl scheme and host above
<activity android:name="com.sap.cloud.mobile.foundation.authentication.OAuth2RedirectActivity">
<intent-filter android:autoVerify="false">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="oauthasservices-youraccountid.hana.ondemand.com"
android:path="/networking_oauth_https_redirect/oauth2redirect" />
</intent-filter>
</activity>
*/
// PersistentTokenStore is a custom implementation of OAuth2TokenStore that
// uses secure store for storing credentials.
val persistentTokenStore = PersistentTokenStore()
ClientProvider.set(OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(OAuth2Interceptor(browserProcessor,
persistentTokenStore)) //uses persistent token store
.cookieJar(WebkitCookieJar())
.build())
// Remember to close the store when application is closed
persistentTokenStore.close()
OAuth Configuration Using Allowlisted Browser¶
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance());
AppHeadersInterceptor appHeadersInterceptor =
new AppHeadersInterceptor();
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
List<ConnectionSpec> connectionSpecs = new LinkedList<>();
ConnectionSpec strict = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build();
connectionSpecs.add(strict);
// Use chrome only (as an allowlisted) browser to process oauth flow
// ClientId and Url values are displayed in SAP BTP cockpit
// (Application security page, click edit for details)
ArrayList<BrowserDetails> allowedBrowsers = new ArrayList<>();
allowedBrowsers.add(BrowserDetails.chrome());//Add other browsers as you like to allow
OAuth2Configuration oAuth2Configuration = new OAuth2Configuration.Builder()
.clientId("48417b17-685d-44d7-8377-4b4030d6be33")
.authUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/authorize")
.redirectUrl("com.sap.template://oauthasservices-youraccountid.hana.ondemand.com")
.tokenUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/token")
.responseType(OAuth2Configuration.CODE_RESPONSE_TYPE)
.build();
OAuth2BrowserProcessor chromeProcessor = new OAuth2BrowserProcessor(
oAuth2Configuration,
null,
new BrowserWhitelist(allowedBrowsers));
/*
Following entry must be added to app `AndroidManifest.xml` to match the redirectUrl scheme above
<activity android:name="com.sap.cloud.mobile.foundation.authentication.OAuth2RedirectActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.sap.template" />
</intent-filter>
</activity>
*/
// PersistentTokenStore is a custom implementation of OAuth2TokenStore that
// uses secure store for storing credentials.
TokenPersistentStore tokenPersistentStore = new TokenPersistentStore();
ClientProvider.set( new OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.cookieJar(new WebkitCookieJar())
.addInterceptor(appHeadersInterceptor)
.addInterceptor(new OAuth2Interceptor(chromeProcessor,
tokenPersistentStore)) //uses persistent token store
.build());
// Remember to close the store when application is closed
tokenPersistentStore.close();
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance())
val appHeadersInterceptor = AppHeadersInterceptor()
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
val connectionSpecs = LinkedList<ConnectionSpec>()
val strict = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build()
connectionSpecs.add(strict)
// Use chrome only (as an allowlisted) browser to process oauth flow
// ClientId and Url values are displayed in SAP BTP cockpit
// (Application security page, click edit for details)
val allowedBrowsers = ArrayList<BrowserDetails>()
allowedBrowsers.add(BrowserDetails.chrome())//Add other browsers as you like to allow
val oAuth2Configuration = OAuth2Configuration.Builder()
.clientId("48417b17-685d-44d7-8377-4b4030d6be33")
.authUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/authorize")
.redirectUrl("com.sap.template://oauthasservices-youraccountid.hana.ondemand.com")
.tokenUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/token")
.responseType(OAuth2Configuration.CODE_RESPONSE_TYPE)
.build()
val chromeProcessor = OAuth2BrowserProcessor(
oAuth2Configuration, null,
BrowserWhitelist(allowedBrowsers))
/*
Following entry must be added to app `AndroidManifest.xml` to match the redirectUrl scheme above
<activity android:name="com.sap.cloud.mobile.foundation.authentication.OAuth2RedirectActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.sap.template" />
</intent-filter>
</activity>
*/
// PersistentTokenStore is a custom implementation of OAuth2TokenStore that
// uses secure store for storing credentials.
val tokenPersistentStore = TokenPersistentStore()
ClientProvider.set(OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.cookieJar(WebkitCookieJar())
.addInterceptor(appHeadersInterceptor)
.addInterceptor(OAuth2Interceptor(chromeProcessor,
tokenPersistentStore)) //uses persistent token store
.build())
// Remember to close the store when application is closed
tokenPersistentStore.close()
OAuth Configuration for Client Credentials Flow¶
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance());
AppHeadersInterceptor appHeadersInterceptor =
new AppHeadersInterceptor();
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
List<ConnectionSpec> connectionSpecs = new LinkedList<>();
ConnectionSpec strict = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build();
connectionSpecs.add(strict);
//ClientId and Url values are displayed in SAP BTP cockpit (Application security page, click edit for details)
OAuth2Configuration credentialsGrantConfiguration = new OAuth2Configuration.Builder()
.clientId("48417b17-685d-44d7-8377-4b4060d6be33")
.tokenUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/token")
.responseType(OAuth2Configuration.GRANT_RESPONSE_TYPE)
.secret("sdk#1")
.build();
OAuth2TokenInMemoryStore oAuth2TokenInMemoryStore = new OAuth2TokenInMemoryStore();
// Configure OAuth2ClientCredentialGrantProcessor interceptor
ClientProvider.set( new OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(new OAuth2Interceptor(
new OAuth2ClientCredentialGrantProcessor(credentialsGrantConfiguration),
oAuth2TokenInMemoryStore)) //Uses in-memory token store
.cookieJar(new WebkitCookieJar())
.build());
// Note: Registering the ActivityLifecycleCallback should be done in your application's
// onCreate method. AppLifecycleCallbackHandler must be registered for the authentication
// interceptors to show UI to authenticate the user.
registerActivityLifecycleCallbacks(AppLifecycleCallbackHandler.getInstance())
val appHeadersInterceptor = AppHeadersInterceptor()
// Only allow TLS 1.2 and 1.3 by using a custom list of ConnectionSpecs.
val connectionSpecs = LinkedList<ConnectionSpec>()
val strict = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
.build()
connectionSpecs.add(strict)
//ClientId and Url values are displayed in SAP BTP cockpit (Application security page, click edit for details)
val credentialsGrantConfiguration = OAuth2Configuration.Builder()
.clientId("48417b17-685d-44d7-8377-4b4060d6be33")
.tokenUrl("https://oauthasservices-youraccountid.hana.ondemand.com/oauth2/api/v1/token")
.responseType(OAuth2Configuration.CODE_RESPONSE_TYPE)
.secret("sdk#1")
.build()
val oAuth2TokenInMemoryStore = OAuth2TokenInMemoryStore()
// Configure OAuth2ClientCredentialGrantProcessor interceptor
ClientProvider.set(OkHttpClient.Builder()
.connectionSpecs(connectionSpecs)
.addInterceptor(appHeadersInterceptor)
.addInterceptor(OAuth2Interceptor(
OAuth2ClientCredentialGrantProcessor(credentialsGrantConfiguration),
oAuth2TokenInMemoryStore)) //Uses in-memory token store
.cookieJar(WebkitCookieJar())
.build())
Disable Authentication Credentials Acquisition UI¶
// Any subsequent challenge from the server will not show the UI till it has been changed.
// This may be needed when app UI context should not be switched for its impact on user
// experience. Note that the pending request that requires authentication
// will fail till credentials are acquired.
AuthenticationUiCallback doNotShowUI = new AuthenticationUiCallback() {
@Override
public boolean allowShowingUiToAuthenticate() {
return false;
}
};
AuthenticationUiCallbackManager.setAuthenticationUiCallback(doNotShowUI);
// Enable showing UI
AuthenticationUiCallback showUI = new AuthenticationUiCallback() {
@Override
public boolean allowShowingUiToAuthenticate() {
return true;
}
};
AuthenticationUiCallbackManager.setAuthenticationUiCallback(showUI);
// Any subsequent challenge from the server will not show the UI till it has been changed.
// This may be needed when app UI context should not be switched for its impact on user
// experience. Note that the pending request that requires authentication
// will fail till credentials are acquired.
val doNotShowUI = AuthenticationUiCallback { false }
AuthenticationUiCallbackManager.setAuthenticationUiCallback(doNotShowUI)
// Enable showing UI
val showUI = AuthenticationUiCallback { true }
AuthenticationUiCallbackManager.setAuthenticationUiCallback(showUI)
File Certificate Provider Implementation¶
class MyFileCertificateProvider implements CertificateProvider {
private static final Logger logger = LoggerFactory.getLogger(MyFileCertificateProvider.class);
private static final String FILE_CERT_PASSWORD = "pass123#";
private X509KeyManager x509KeyManager;
private String alias;
public MyFileCertificateProvider(Context context) {
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(context.getResources().openRawResource(R.raw.mycert),
FILE_CERT_PASSWORD.toCharArray());
Enumeration<String> enumeration = keyStore.aliases();
while (enumeration.hasMoreElements()) {
String alias = enumeration.nextElement();
Certificate[] certificate = keyStore.getCertificateChain(alias);
if (certificate != null && certificate.length > 0
&& certificate[0] instanceof X509Certificate) {
this.alias = alias;
break;
}
}
KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, FILE_CERT_PASSWORD.toCharArray());
for (KeyManager keyManager : keyManagerFactory.getKeyManagers()) {
if (keyManager instanceof X509KeyManager) {
x509KeyManager = (X509KeyManager) keyManager;
break;
}
}
} catch (Exception e) {
logger.error("Exception occurred.." + e.getMessage());
}
}
@Override
public void clear(String host, int port) {
}
@Override
public void clear() {
}
@Override
public void onCertificateRequest(CertificateRequest request) {
request.proceed(x509KeyManager.getPrivateKey(alias),
x509KeyManager.getCertificateChain(alias));
}
}
internal class MyFileCertificateProvider(context: Context) : CertificateProvider {
private var x509KeyManager: X509KeyManager? = null
private var alias: String? = null
init {
try {
val keyStore = KeyStore.getInstance("PKCS12")
keyStore.load(
context.resources.openRawResource(R.raw.mycert),
FILE_CERT_PASSWORD.toCharArray()
)
val enumeration = keyStore.aliases()
while (enumeration.hasMoreElements()) {
val alias = enumeration.nextElement()
val certificate = keyStore.getCertificateChain(alias)
if (certificate != null && certificate.size > 0
&& certificate[0] is X509Certificate
) {
this.alias = alias
break
}
}
val keyManagerFactory = KeyManagerFactory
.getInstance(KeyManagerFactory.getDefaultAlgorithm())
keyManagerFactory.init(keyStore, FILE_CERT_PASSWORD.toCharArray())
for (keyManager in keyManagerFactory.keyManagers) {
if (keyManager is X509KeyManager) {
x509KeyManager = keyManager
break
}
}
} catch (e: Exception) {
logger.error("Exception occurred.." + e.message)
}
}
override fun clear(host: String, port: Int) {}
override fun clear() {}
override fun onCertificateRequest(request: CertificateRequest) {
request.proceed(
x509KeyManager!!.getPrivateKey(alias),
x509KeyManager!!.getCertificateChain(alias)
)
}
companion object {
private val logger = LoggerFactory.getLogger(MyFileCertificateProvider::class.java)
private val FILE_CERT_PASSWORD = "pass123#"
}
}
Basic Authentication Credential Store Using Secure Store¶
class PersistentCredentialStore implements BasicAuthCredentialStore {
final String KEY_PREFIX = "key prefix";
final String STORE_NAME = "secure store name";
final Logger logger = LoggerFactory.getLogger(PersistentCredentialStore.class);
SecureKeyValueStore mSecureStore;
PersistentCredentialStore(Context context) {
mSecureStore = new SecureKeyValueStore(context, STORE_NAME);
try {
mSecureStore.open(null);// A key is generated and stored in Android KeyStore
} catch (OpenFailureException e) {
logger.error("Store already exists with non-default key for no passcode!", e);
}
}
@Override
public void storeCredential(String host, String realm, String[] credentials) {
mSecureStore.put(key(host, realm), credentials);
}
@Override
public String[] getCredential(String host, String realm) {
return mSecureStore.getSerializable(key(host, realm));
}
@Override
public void deleteCredential(String host, String realm) {
mSecureStore.remove(key(host, realm));
}
@Override
public void deleteAllCredentials() {
mSecureStore.removeAll();
}
private String key(String host, String realm) {
return KEY_PREFIX + host + "::" + realm;
}
public void close() {
mSecureStore.close();
}
}
internal class PersistentCredentialStore(context: Context) : BasicAuthCredentialStore {
val KEY_PREFIX = "key prefix"
val STORE_NAME = "secure store name"
val logger = LoggerFactory.getLogger(PersistentCredentialStore::class.java)
var mSecureStore: SecureKeyValueStore
init {
mSecureStore = SecureKeyValueStore(context, STORE_NAME)
try {
mSecureStore.open(null)// A key is generated and stored in Android KeyStore
} catch (e: OpenFailureException) {
logger.error("Store already exists with non-default key for no passcode!", e)
}
}
override fun storeCredential(host: String, realm: String, credentials: Array<String>) {
mSecureStore.put(key(host, realm), credentials)
}
override fun getCredential(host: String, realm: String): Array<String> {
return mSecureStore.getSerializable(key(host, realm))
}
override fun deleteCredential(host: String, realm: String) {
mSecureStore.remove(key(host, realm))
}
override fun deleteAllCredentials() {
mSecureStore.removeAll()
}
private fun key(host: String, realm: String): String {
return "$KEY_PREFIX$host::$realm"
}
fun close() {
mSecureStore.close()
}
}
OAuth Token Store Using Secure Store¶
class PersistentTokenStore implements OAuth2TokenStore {
final String KEY_PREFIX = "key prefix";
final String STORE_NAME = "secure store name";
final Logger logger = LoggerFactory.getLogger(PersistentTokenStore.class);
SecureKeyValueStore mSecureStore;
PersistentTokenStore(Context context) {
mSecureStore = new SecureKeyValueStore(context, STORE_NAME);
try {
mSecureStore.open(null);// A key is generated and stored in Android KeyStore
} catch (OpenFailureException e) {
logger.error("Store already exists with non-default key when trying to skip passcode!", e);
}
}
@Override
public void storeToken(OAuth2Token oauth2Token, String url) {
mSecureStore.put(tokenKey(url), oauth2Token);
}
@Override
public OAuth2Token getToken(String url) {
return mSecureStore.getSerializable(tokenKey(url));
}
@Override
public void deleteToken(String url) {
mSecureStore.remove(tokenKey(url));
}
private String tokenKey(String url) {
return KEY_PREFIX + Uri.parse(url).getHost();
}
public void close() {
mSecureStore.close();
}
}
internal class PersistentTokenStore(context: Context) : OAuth2TokenStore {
val KEY_PREFIX = "key prefix"
val STORE_NAME = "secure store name"
val logger = LoggerFactory.getLogger(PersistentTokenStore::class.java)
var mSecureStore: SecureKeyValueStore
init {
mSecureStore = SecureKeyValueStore(context, STORE_NAME)
try {
mSecureStore.open(null)// A key is generated and stored in Android KeyStore
} catch (e: OpenFailureException) {
logger.error("Store already exists with non-default key when trying to skip passcode!", e)
}
}
override fun storeToken(oauth2Token: OAuth2Token, url: String) {
mSecureStore.put(tokenKey(url), oauth2Token)
}
override fun getToken(url: String): OAuth2Token {
return mSecureStore.getSerializable(tokenKey(url))
}
override fun deleteToken(url: String) {
mSecureStore.remove(tokenKey(url))
}
private fun tokenKey(url: String): String {
return KEY_PREFIX + Uri.parse(url).host
}
fun close() {
mSecureStore.close()
}
}
Last update: December 16, 2020