Working with secure ArcGIS services

Complexity: Beginner Data Requirement: ArcGIS Tutorial Data for Desktop

Layers and Tasks in ArcGIS API for Android communicate with ArcGIS Server web services. These services may be secured to permit access to only authorized users. An ArcGIS Server instance can use one of two authentication methods: token-based authentication or HTTP (including Windows) authentication. Both types of authentication modes are supported by the API. The information below will assist you once you have the connection and login information.

If you are the administrator of an ArcGIS Server system and wish to restrict access to your ArcGIS Web services, information is available in the "Securing Internet connections and Web applications" chapter in the ArcGIS Server Help. The Help is available on your server or online at ESRI for both .NET and Java.

Services secured with token-based authentication

Services secured with token-based authentication require that a token be included in each request for a map, query, and so on. To use a secure service through the API, you need to know the credentials (username and password) or tokens to access the service. The server administrator can provide this information. Once you know the credentials or tokens to use, you need to pass them to the layer or the task through a UserCredentials object in the Java code or hardcode the tokens in the layout xml by including a long-term token directly in the URL. The following examples demonstrate several different ways to access secure services.

You should instantiate the layer or the task with credentials through UserCredentials object in Java code. For example:

UserCredentials creds = new UserCredentials();
creds.setUserAccount("username", "password ");
// The following line can be omitted if the default token service is used.
creds.setTokenServiceUrl("http://hostname/ArcGIS/tokens");	
ArcGISDynamicMapServiceLayer layer = new ArcGISDynamicMapServiceLayer(
  "http://servicesbeta.esri.com/ArcGIS/rest/services/SanJuan/TrailConditions/MapServer",
  null,
  creds);

Alternatively, instead of passing username and password you can pass in a token obtained from the corresponding token service as so.

UserCredentials creds = new UserCredentials();
creds.setUserToken("token", "referer");
Locator al = new Locator(
  "http://xxx.esri.com/ArcGIS/rest/services/SanFranciscoLocator/GeocodeServer",
  creds);

The API will automatically try to discover the URL of the token service where tokens can be acquired. If you know this information in advance, you can provide it to the UserCredentials object as shown in the above example so that it does not make any unnecessary network requests to discover the same information.

Using self-signed certificates

To safeguard content exchanged over the network from attacks, HTTPS should be used whenever supported by the service. HTTPS connections use Secure Sockets Layer (SSL) to encrypt information that is exchanged over the network and digital certificates to verify identities of the parties involved. The ArcGIS API for Android supports both certificates issued by a trusted certificate authority and self-signed certificates. The following example shows the usage of a self-signed certificate.

// Load self-signed certificate
KeyStore keyStore = KeyStore.getInstance("BKS");
InputStream is = this.getResources().openRawResource(R.raw.xxx);
keyStore.load(is, "xxx".toCharArray());
// Populate Security Info
UserCredentials creds = new UserCredentials();
creds.setUserAccount("username", "password");
UserCredentials.setTrustStore(keyStore);
ArcGISFeatureLayer layer = new ArcGISFeatureLayer(
  "https://xxx.esri.com/ArcGIS/rest/services/dc_fire_sde/FeatureServer/1",
  MODE.ONDEMAND,
  creds);

Services secured with HTTP/Windows authentication

When a request is made to a service secured with HTTP authentication (HTTP basic or HTTP digest), a user credentials must be supplied to the service through UserCredentials object. For Example:

UserCredentials creds = new UserCredentials();
creds.setUserAccount("xxx", "xxx");                         
Geoprocessor gp = new Geoprocessor(
  "https://hostname/ArcGIS/rest/services/BufferService/GPServer/BufferPoints",
  creds);

Exception Handling

When a layer is failed to be loaded to a map due to security, both the layer and the map will send out EsriSecurityException error. Users can listen to the status changed events on MapView or Layer to handle the error. You can listen to the LAYER_LOADING_FAILED on MapView or INITIALIZATION_FAILED on Layer and get the error detail through getError() method on STATUS. A layer can be re-initialized with a given credential. If the layer is re-initialized successfully, it will be reloaded into the map automatically. The following is an example of exception handling through MapView.

MapView map;
ArcGISFeatureLayer fl;

public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  map = new MapView(this);
  String url = ...;
  fl = new ArcGISFeatureLayer(url, MODE.ONDEMAND);
  map.addLayer(fl);
  setContentView(map);
  ...
  // Handle status change event on MapView
  map.setOnStatusChangedListener(new OnStatusChangedListener() {
    private static final long serialVersionUID = 1L;

    public void onStatusChanged(Object source, STATUS status) {
      // Check if a layer is failed to be loaded due to security
      if (status == STATUS.LAYER_LOADING_FAILED) {
        if ((status.getError()) instanceof EsriSecurityException) {
          EsriSecurityException securityEx = (EsriSecurityException) status.getError();
          if (securityEx.getCode() == EsriSecurityException.AUTHENTICATION_FAILED)
            Toast.makeText(map.getContext(), "Authentication Failed!", 
                Toast.LENGTH_SHORT).show();
          else if (securityEx.getCode() == EsriSecurityException.TOKEN_INVALID)
            Toast.makeText(map.getContext(), "Invalid Token!", Toast.LENGTH_SHORT).show();
          else if (securityEx.getCode() == EsriSecurityException.TOKEN_SERVICE_NOT_FOUND)
            Toast.makeText(map.getContext(), "Token Service Not Found!", 
                Toast.LENGTH_SHORT).show();
          else if (securityEx.getCode() == EsriSecurityException.UNTRUSTED_SERVER_CERTIFICATE)
            Toast.makeText(map.getContext(), "Untrusted Host!", Toast.LENGTH_SHORT).show();
              
          if (source instanceof ArcGISFeatureLayer) {
            // Set user credential through username and password
            UserCredentials creds = new UserCredentials();
            creds.setUserAccount("username", "password");
            fl.reinitializeLayer(creds); 
          }                
        }						
      }
    }        
  });  
  ...

When an operational layer of a WebMap is failed to be loaded due to security, the map will send out EsriSecurityException error. Users can listen to the WebMap loading events to handle the error as so.

MapView map;

public void onCreate(Bundle savedInstanceState) {
  ...
  OnWebMapLoadListener listener = new OnWebMapLoadListener() {

    public MapLoadAction<UserCredentials> onWebMapLoadError(MapView source,
      WebMap webmap, WebMapLayer wmlayer, Layer layer, Throwable error,
      UserCredentials credentials) {
      if (error instanceof EsriSecurityException) {
        UserCredentials creds;
        creds = new UserCredentials();
        creds.setUserAccount("username", "password");

        if (credentials != null && (credentials.getUserName() == creds.getUserName() 
            && credentials.getPassword() == creds.getPassword())) 
          return null;
										
        return new MapLoadAction<UserCredentials>(Action.CONTINUE_OPEN_WITH_THE_PARAMETER, creds);
      }
      return null;
   }

   public void onWebMapLayerAdd(MapView source, WebMap webmap,
     WebMapLayer wmlayer, Layer layer, UserCredentials credentials) {
     ...
   }

  };

  String url = ...;
  map = new MapView(this, url, "", "", "", listener);
  ...
 }
5/31/2012