Secure services

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 services using credentials

If you know the credentials (user name and password) needed to access a secure service, pass these credentials to the layer/task through an AGSCredential object.

//create the credential
let cred = AGSCredential(user: "<user>", password: "<password>")
//pass the credential to layer or task
let layer = AGSDynamicMapServiceLayer(URL: url, credential: credential)
let task = AGSQueryTask(URL: url, credential: cred)
The API tries to discover the authentication type (token or HTTP) used by the service.

HTTP authentication

If the service is using HTTP authentication, the credentials (user name and password) propagate to the service via HTTP request headers.

Token authentication

If the service is using token-based authentication, the API attempts to discover the URL of the token service where tokens can be acquired. If you know this information in advance, provide it to the AGSCredential object so that the API does not make any unnecessary network requests to discover the same information.

let cred = AGSCredential(user: "<user>", 
 password: "<password>", 
 authenticationType: .Token)

The layer/task propogates your credentials to the token service and transparently acquires a token on your behalf. The token is then included in every request to the service. When the token expires, the layer/task automatically acquires a fresh token.

Access secure services using client certificates

As an alternative to employing user credentials (user name/password combination) or tokens, use digital certificates to access services 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 client certificate), which can be used to confirm their 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, Thawte, 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/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 OS X computer so that your custom app or apps can use it, do the following:

  1. Make the certificate available on the computer.
  2. Progamatically import the certificate into the keychain access group of your app.

There are many approaches you can take, but two options are discussed below.


If the user has email configured on the computer, the user's certifcate can be emailed to them as an attachment. However, if the certificate uses the default *.p12 or *.pfx file extension, the OS X system attempts 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 acess the default keychain. Custom apps do not get access to the default keychain, and as a result, your app won't be able to access the certificate.

To work around this problem, rename the certificate file to use a custom file extension, for example, *.myapp. Your custom app must also declare that it can handle files with such extensions. Now when a user taps the certificate attached in the email, your app invokes, and the certificate is passed to the app delegate. The app delegate can then import the certificate into the app's keychain access group as shown below:

func application(application: UIApplication, openURL url: NSURL, sourceApplication: String, annotation: AnyObject?) -> Bool { 
 //Read the contents of the certificate file as bytes 
 let PKCS12Data = NSData(contentsOfURL: url)
 //Import the certificate
 var error:NSError? = nil
 let idRef = AGSCredential.importCertificateData(PKCS12Data, password: "<cert_password>", overwrite: true, error: &error)
 return true

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

Access the secure service

Once the certificate installs on the device, use the identity within it to access services that are secured using PKI. To do this, initialize the AGSCredential object using a reference to the identity in the certificate.

//Reference to the identity in a certificate
 let idRef:SecIdentityRef = ...
 let cred = AGSCredential(identityRef: idRef)

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

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

func layer(layer: AGSLayer!, didFailToLoadWithError error: NSError!) {
 if error.ags_isAuthenticationError() {
  let pSpace = (error.userInfo)["protectionSpace"] as NSURLProtectionSpace
  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
   let identities = AGSCredential.identitiesForProtectionSpace(pSpace)
   //if we find more than one identity, display the list to the user, ask them to pick one
   let selectedIdentity = identities[selectedIndex] as SecIdentityRef
   //try to reload layer with the selected one
   let cred = AGSCredential(identityRef: selectedIdentity)
   (layer as AGSFeatureLayer).resubmitWithURL(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 when supported by the service. This ensures 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.

Use 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.

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

If you chose to trust a server that is using a self-signed certificate, for instance, if that server is used for development purposes, add the host name of the server to the trustedHosts property on the NSURLConnection class.

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

func layer(layer: AGSLayer!, didFailToLoadWithError error: NSError!) {
 //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
  let tiledLayer = layer as AGSTiledMapServiceLayer
  //Add the server to the trusted hosts list
  //Reload the layer
  tiledLayer.resubmitWithURL(tiledLayer.URL, credential:credential)

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

func application(application: NSApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
 // Override point for customization after application launch.