Skip to content

Backend Connectivity

Much of the Foundation APIs' functionality is closely tied to the network communication to the backend servers. While Foundation APIs make it easier for developers to write applications that interact with servers, the APIs rely on the okHttp library for network communication. In order to give developers using the foundation library more control, the classes that make networking calls use the OkHttpClient that is directly given to them, or fall back to use the OkHttpClient that is set globally on ClientProvider.

Authentication

When a mobile application is configured with an authentication method, any access to the backend that uses REST API requires an authenticated session context or valid authentication headers for authentication. Authentication modules provided with the SDK help you with authentication.

Typical usage of OkHttpClient involves setting up an instance with the necessary authenticator or interceptor and passing that instance for OkHttpClient parameter in for any method calls that interact with mobile services.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
...
...
OkHttpClient mobileServiceNetworkingClient = new OkHttpClient.Builder()
    .addInterceptor(new SamlInterceptor(samlConfiguration))
    .cookieJar(new WebkitCookieJar())
    .build();
...
Settings mobileAppSettings = new Settings(mobileServiceNetworkingClient, mobileAppParameters);
mobileAppSettings.load();
...
...
RemoteNotificationClient mobileServicePushClient = new RemoteNotificationClient(mobileServiceNetworkingClient, mobileAppParameters);

Foreground Activity Tracking

When establishing a new session, authentication modules may need to interact with the user to obtain credential information. To accomplish this, the modules need to know which activity is in the foreground. The Foundation component has a AppLifeCycleCallbackHandler singleton that is used to find which activity is in the foreground.

This callback handler must be registered using android.app.Application.registerActivityLifecycleCallbacks before any networking calls are made. Once registered, the ApplLifeCycleCallbackHandler will be able to keep track of the foreground activity.

See the Authentication topic for more information.

Mobile Service Http Headers

Some functionality in mobile services depend on special http headers. The presence of these headers in a request affects certain functions like logout.

Header Description
X-SMP-APPID The application id of the mobile app as configured in SAP Cloud Platform Mobile Services
X-SMP-DEVICEID Device Id
X-SMP-APP-VERSION The mobile application version
X-SMP-SDK-VERSION The version of the SDK the app is using

AppHeadersInterceptor

The foundation library provides AppHeadersInterceptor that automatically adds these headers to the okHttp request if they are not already present. In addition to the headers specifically for Mobile Service, AppHeadersInterceptor also adds the Accept-Language header to requests that do not already have one. The Accept-Language header added by AppHeadersInterceptor is of the form "LA-CO, LA;q=0.9" where "LA" is the language code of the device's locale, and "CO" is the country code of the device's locale.

To use this class, add an AppHeadersInterceptor when building an OkHttpClient. The following example shows how to use AppHeadersInterceptor in a "no-auth" configuration. Please note that usually this interceptor is added in addition to the other authentication interceptors.

1
2
3
4
5
6
// Construct AppHeadersInterceptor using a SettingsParameter object.
AppHeadersInterceptor appHeadersInterceptor = AppHeadersInterceptor(settingsParameters);

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(appHeadersInterceptor)
    .build();

Cross-Site Request Forgery Protection

If you want to use Cross-Site Request Forgery (CSRF) protection for your application, first you must enable this feature in the mobile services cockpit. See: Defining Application Security for more information.

If CSRF protection is enabled, you need to send a X-CSRF-Token header for the modifying HTTP requests. The CsrfTokenInterceptor can automatically handle this for every request.

Note

The CSRF Protection option protects all services, such as registration, with CSRF tokens. Proxied endpoints are not protected, since they may be protected on the back end.

Basic usage

The following example shows how to add the CsrfTokenInterceptor if you have one root URL for every CSRF token request. The rootUrl parameter is the URL from which the CSRF token will be requested.

1
2
3
4
5
String rootUrl = "https://myserver.hana.ondemand.com/odata/applications/v4/myappid/";

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new CsrfTokenInterceptor(rootUrl))
    .build()

Advanced usage

If you have multiple CSRF protected backends and these backends accept different CSRF tokens, you must implement a CsrfTokenUrlProvider to provide CSRF URL for different request URLs. For token storing, you should implement a CsrfTokenStore. A default implementation of the store is provided that will keep a token per host and port.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MyCsrfTokenUrlProvider implements CsrfTokenUrlProvider {
    @Override
    public String getTokenUrl(String url) {
        // TODO: implement custom logic to create token URL from the request URL
        return ...
    }
}

public class MyCsrfTokenStore implements CsrfTokenStore {
    @Override
    public void setToken(String token, String url) {
        // TODO: implement
    }

    @Override
    public String getToken(String url) {
        // TODO: implement
        return ...
    }

    @Override
    public void deleteToken(String url) {
        // TODO: implement
    }
}

For more information about how this protection works, see the following topics:

User Blocking

An administrator can block a particular user by setting a condition at the server. A blocked user is unable to register an application, receive data, or both. When a user is blocked, all http requests from that user’s applications receive a 403 (Forbidden) status response with an X-MESSAGE-CODE header. The header value indicates whether registration, traffic, or both is blocked. At this point, the application can no longer communicate with the server and should notify the user that he is blocked.

The foundation library provides the BlockedUserInterceptor class to facilitate handling this condition. It is constructed with a callback function and added to the interceptor chain of the OkHttpClient. The specified callback is invoked on the first receipt of a 403 response with the X-MESSAGE-CODE header from the server. Subsequent reception of the blocked response will not trigger the callback again until a successful response is received or the application is restarted.

The callback will be supplied with an enum value representing the blocked type that was in the response header.

X-MESSAGE-CODE Value Summary

enum Value Header Value Description
UNDEFINED_BLOCK N/A An unrecognized value was received
REGISTRATION_BLOCKED REG_BLOCKED All application registrations by this user are blocked
TRAFFIC_BLOCKED TRAFFIC_BLOCKED All data for this user is blocked
TRAFFIC_AND_REGISTRATION_BLOCKED TRAFFIC_REG_BLOCKED Both registration and data for this user is blocked
REGISTRATION_WIPED REGISTRATION_WIPED This user's registration at the server has been wiped

Please note that the callback is invoked synchronously. Therefore, it needs to return in a timely, non-blocking manner. The developer is free to start a thread or an activity in the callback if that is necessary.

To use this class, add a BlockedUserInterceptor to the OkHttpClient instance. Please note that usually this interceptor is added in addition to the other interceptors.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Construct BlockedUserInterceptor supplying a callback function.
OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new BlockedUserInterceptor( new BlockedUserInterceptor.BlockedUserCallback(){

            @Override
            public void handleBlockedUser(BlockedUserInterceptor.BlockType blockType) {
                // TODO: inform the user that they are blocked by the administrator
            }
        } ))
    .build();

Lock and Wipe

Lock and Wipe is a policy in the setting data provided by the server. This policy specifies the number of days that are allowed to elapse without authenticating with the server before the application is locked or its data is wiped. The settings data from the server is returned by the Settings object in the foundation library.

The Lock and Wipe Policy is set for the application at the server. This is labeled "blockWipingPolicy" in the JSON that is sent down to the client. Lock is named "blockDisconnectedPeriod" and wipe is "wipeDisconnectedPeriod". Both values are in days.

If "blockWipeEnabled" is true and either or both of these are greater than zero, then the corresponding action is taken if the application fails to connect to the server within the specified number of days.

Presumably, the wipe period is longer than the lock period. If the lock period is exceeded, then the application should be locked until the user successfully connects to the server.

If the wipe period is exceeded, then the application data should be reset.

A LockWipePolicy class is provided by the foundation library. The "blockWipingPolicy" is parsed by the Settings object. The values in mobileservices/settingsExchange/blockWipingPolicy are used to construct a LockWipePolicy object. These previously described values are:

  • "wipeDisconnectedPeriod"
  • "blockWipeEnabled"
  • "blockDisconnectedPeriod"

Two callback methods can also be passed to the LockWipePolicy constructor in addition to these values. These are lockCallback and wipeCallback which handle the lock and wipe conditions respectively. Alternatively, the LockWipePolicy can be constructed without the callbacks and they can be set at a later time by using setLockCallback and setWipeCallback.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Lock and Wipe policy values parsed and extracted through the Setting object JSON
JSONObject settingsJson;    // result from Settings.CallbackListener.onSuccess()
boolean enabled = false;
int lockDays = 0;
int wipeDays = 0;

JSONObject lockWipePolicyJson = settingsJson.optJSONObject("blockWipingPolicy");
if (lockWipePolicyJson != null) {
    enabled = lockWipePolicyJson.optBoolean("blockWipeEnabled", false);
    lockDays = lockWipePolicyJson.optInt("blockDisconnectedPeriod", 0);
    wipeDays = lockWipePolicyJson.optInt("wipeDisconnectedPeriod", 0);
}

// Construct the Lock Wipe Policy supplying two callback functions.

LockWipePolicy lockWipePolicy = new LockWipePolicy(
    enabled,
    lockDays,
    wipeDays,
    new LockWipePolicy.LockCallback() {

        @Override
        public void handleLock() {
            // TODO: Inform the user that the application is locked due to not connecting with the server
        }
    },
    new LockWipePolicy.WipeCallback() {

        @Override
        public void handleWipe() {
            // TODO: Reset the application data
        }
    }
);

A call to LockWipePolicy.apply() must be made after the client policy is retrieved through the Settings object and the LockWipePolicy object is constructed and the callbacks assigned. The respective callback will be invoked in the case where the lock or wipe intervals have been exceeded. Wipe is evaluated first. If it is zero or within range then lock is evaluated. In this way, only one of the callbacks can be called for a single invocation of LockWipePolicy.apply().

1
2
3
4
5
6
7
8
@NonNull
Date retrieveLastConnectionTime() {
    Date lastConnectionTime;
    // TODO: Retrieve the persisted the last connection time
    return lastConnectionTime;
}

lockWipePolicy.apply(retrieveLastConnectionTime());

The call to LockWipePolicy.apply() requires the last connection time. This corresponds to the last time that the application communicated with the server. To accommodate this, the application must save the current time (GMT) in the application secure store each time it communicates with the server. This is facilitated by the LastConnectionTimeInterceptor class provided by the foundation library. It is constructed with a callback function and added to the interceptor chain of the OkHttpClient. The specified callback is invoked every time a success (200) response is received from the server.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Construct LastConnectionTimeInterceptor supplying a callback function.
OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new LastConnectionTimeInterceptor( new LastConnectionTimeInterceptor.LastConnectionTimeCallback(){

            @Override
            public void updateLastConnectionTime() {
                Date lastConnectionTime = new Date();
                // TODO: Persist the last connection time
            }
        } ))
    .build();