Skip To Content

Access secure services

In this topic

Resources on a server, such as services, layers, webmaps, or items may be secured to permit access to authorized users only. API classes such as AGSPortal, AGSWebMap, AGSLayer, AGSTask, etc which are initialized with a URL to a server-side resources, also optionally accept a credential that can be used to access that resource if it is secured.

Access secure resources using credentials

If you know the credentials (username and password) needed to access a secure resource, you can pass these credentials to the API through an AGSCredential object.

//create the credential
AGSCredential* cred = [[AGSCredential alloc] initWithUser:@"<user>" password:@"<password>"];    

//pass the credential to layer or task
AGSDynamicMapServiceLayer* layer = [AGSDynamicMapServiceLayer dynamicMapServiceLayerWithURL:url credential:cred ];    
AGSQueryTask* task = [AGSQueryTask queryTaskWithURL:url credential:cred];
The API will try to discover the type of authentication being used by the resource.

HTTP authentication

If the service is using HTTP authentication, the credentials (username and password) will be propagated to the service via request headers as specified in the HTTP specification. Both Basic and Digest authentication schemes are supported.

Token authentication

If the service is using ArcGIS token-based authentication, the API will attempt to discover the URL of the token service where tokens can be acquired. The API will securely and transparently acquire a token on your behalf using the credentials you provided and the token will be included in every request to the resource. When the token expires, the API will automatically acquire a fresh token.

AGSCredential* cred = [[AGSCredential alloc] 
   initWithUser:@"<user>" password:@"<password>" authenticationType:AGSAuthenticationTypeToken tokenUrl:url
 ];

If you do not feel comfortable in letting the API acquire a token using the credentials, you can pre-acquire a token, or acquire it yourself through other means, and use it to initialize an AGSCredential object. In this scenario, it is your responsibility to periodically acquire a new token or ensure that the token is valid for the life of the application otherwise things will stop working when the token expires.

//when using a short lived token
AGSCredential* cred = [[AGSCredential alloc] initWithToken:@"<token>"] ;

//when using a long lived token.
//also need to provide the referrer used when generating the token
 AGSCredential* cred = [[AGSCredential alloc] initWithToken:@"<token>" referer:@"<referer>"] ;

Access secure resources using client certificates

As an alternative to using user credentials (username and password combination) or tokens, you can use digital certificates to access resources that are secured using HTTP authentication and Public Key Infrastructure (PKI). In such a scenario, each user who is authorized to access the service is provided a digital certificate (also known as a client certificate) that can be used to confirm his or her identity when connecting to the service.

A certificate contains information about a user's identity and is usually endorsed or signed by a trusted entity, commonly known as a certification authority (CA). The entity can be external—such as Verisign, Thwate, and so on—or it can be internal to the organization. The certificate also contains additional information—such as its validity period, a serial number, public or private key combination, and so on—stored according to the x.509 format. The certificate file usually has a .p12 or .pfx file extension and must be installed on the user's device.

Install the certificate

To install a certificate on an iOS device so that your custom app(s) can use it, do the following:

  • Make the certificate available on the device.
  • Programmatically import the certificate into the keychain access group of your app.

There are many approaches you can take, but two recommended options are described here.

Email

If the user has email configured on the device, the user's certificate can be emailed to him or her as an attachment. However, if the certificate uses the default .p12 or .pfx file extension, the iOS system will attempt to import the certificate into the default keychain when a user tries to open the attachment. Only system apps, such as Mail and Safari, can access the default keychain. Custom apps do not get access to the default keychain; as a result, your app won't be able to access the certificate. (This post contains more information.)

To work around this problem, you need to rename the certificate file to use a custom file extension, such as .myapp. Your custom app must also declare that it can handle files with such extensions. When a user taps the certificate attached in the email, your app will be invoked, and the certificate will be passed along to the app delegate. The app delegate can then import the certificate into the app's keychain access group.

-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
  //Read the contents of the certificate file as bytes
 	NSData *PKCS12Data = [[NSData alloc] initWithContentsOfURL:url];

	 //Import the certificate 
  NSError *error = nil;
  SecIdentityRef idRef = [AGSCredential importCertificateData:PKCS12Data password:@"<cert_password>" overwrite:YES error:&error];
}
Note:

Digital certificates are usually protected by a password to prevent unauthorized use. This password is required to import the certificate, and you many need to prompt the user for it if you don't have it already.

iTunes file sharing

You can also allow a user to transfer the digital certificate (.p12 or .pfx file) to the device through iTunes file sharing. Your app can then access the certificate from the app's Documents directory and import it into the keychain in a manner similar to the previously shown code.

To enable file sharing for your application, you must modify your application's Info.plist file by setting the UIFileSharingEnabled key (displayed as "Application supports iTunes file sharing") to true. Any files copied through file sharing are made available in the Documents directory of the application. For more information about the iOS file system, see the Apple File System Programming Guide.

Learn more about iTunes file sharing

Access the secured resource

Once the certificate is installed on the device, you can use the identity within it to access resources that are secured using PKI. To do this, you need to initialize the AGSCredential object using a reference to the identity in the certificate.

//Reference to the identity in a certificate
SecIdentityRef idRef = ...;


AGSCredential* cred = [[AGSCredential alloc] initWithIdentityRef:idRef];

Note:

You get the reference to the identity when you import the certificate into the keychain as shown in the previous code.

Alternatively, you can get a list of identities for certificates already imported into the keychain. You can then display this list to the user and ask him or her to pick the one to use. To get a list of identities that you can use with a service, you need to know the protection space of the service. The following code shows how to handle a scenario in which a layer fails to load because of authentication failure while accessing a PKI secured service:

- (void)layer:(AGSLayer *)layer didFailToLoadWithError:(NSError *)error {
    
if ([error isAuthenticationError]) {

		NSURLProtectionSpace* pSpace = (error.userInfo)[@"protectionSpace"];
  if (pSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate) {

				//list of identities that can be used with the service
				//you can pass in nil if you want all identities regardless of 
    NSArray *identities = [AGSCredential identitiesForProtectionSpace:pSpace];

    //if we find more than one identity, display the list to the user, ask them to pick one
				SecIdentityRef selectedIdentity = identities[selectedIndex];

				//try to reload layer with the selected one
	    AGSCredential *cred = [[AGSCredential alloc] initWithIdentityRef:selectedIdentity];
    [layer resubmitWithURL:layer.URL credential:cred];
  }
}

As you can see, the SDK does most of the heavy lifting to shield you from the low-level system details. See Apple's documentation for information on dealing with certificates, keychains, and identities.

Encrypting data using SSL

To safeguard content exchanged over the network from eavesdroppers and man-in-the-middle attacks, use HTTPS whenever supported by the service. This will ensure that your data, credentials, or tokens are not compromised. HTTPS connections use Secure Sockets Layer (SSL) to encrypt information exchanged over the network and digital certificates to verify identities of the parties involved.

Handling self-signed certificates

There may be times when you encounter servers using a self-signed certificate with SSL. As a security measure, HTTPS connections are not established with a server that uses self-signed certificates. Digital certificates are a means to prove the identity of an entity and should ideally be endorsed (signed) by a known, trusted authority to be credible. Anyone can create a digital certificate and sign it to establish a misleading identity.

Whenever the API fails to establish an HTTPS connection, it provides an NSError object that can be inspected to check whether the failure was because the server was using a self-signed certificate.

If you choose to trust a server that is using a self-signed certificate, for instance, if that server is being used for development purposes, you can add the hostname of the server to the ags_trustedHosts property on NSURLConnection class.

The following code snippet checks to see if a layer failed to load because the server was using a self-signed certificate, and if so, adds the server to ags_trustedHosts and reloads the layer:

- (void)layer:(AGSLayer *)layer didFailToLoadWithError:(NSError *)error {

  //if error was because server was using a self-signed certificate
  if ([error code]== NSURLErrorServerCertificateUntrusted) {

   //You may want to prompt the user and ask if it is okay to establish connection with the server
   ...

   //Assuming here that the layer was a tiled layer. 
   //You will need to change this code to typecast it to the correct type
   AGSTiledMapServiceLayer* tiledLayer = (AGSTiledMapServiceLayer*)layer;
   
   //Add the server to the trusted hosts list
   [[NSURLConnection ags_trustedHosts] addObject:[tiledLayer.URL host]];
   
   //Reload the layer
   [tiledLayer resubmitWithURL:tiledLayer.URL credential:scredential];
  }
}

Alternatively, if you know in advance that the server you will be connecting to uses a self-signed certificate, you can add it to the trustedHosts list as soon as the application starts. The following code snippet does so in the application delegate:

- (void)applicationDidFinishLaunching:(UIApplication *)application {

  ...

 [[NSURLConnection ags_trustedHosts] addObject:@"<my_server>"];


}