If you’ve ever tried to set up a complex email account on an iOS device, you know how difficult it can be for a non-technical user. And a long ‘how-to’ support document doesn’t really make things any easier. To truly encourage adoption of your mobile app, you need to be able to set things up for them – not send them off to do it themselves.
One of the few options to interact with settings on an Apple device remotely is via configuration profiles. With configuration profiles you can do everything from setting user’s wallpaper to altering email accounts on iOS devices (remember LinkedIn Intro?) and changing cellular network settings.
At FullContact, we’re in the process of implementing automated FullContact CardDAV address book installation on Apple devices. The end result makes life much simpler for end users – but not for the developer, as configuration profiles lack practical technical insight from the trenches.
Read on to find out some of the issues we encountered along the way, and how you can use configuration profiles to improve your configuration processes.
What’s inside a configuration profile?
A configuration profile is an XML file (in Apple’s own plist format), that contains information about the distributer of the profile and the config information for the OS X or iOS device that installs it.
In most cases, installing a configuration profile means to bootstrap subsequent processes (e.g., adding a new calendar source and starting to fetch events from a source listed in the profile). Configuration profile installation consists of the following steps:
- User (directly or via app) sends an HTTP request to profile installation service;
- Service returns a signed XML payload with the data specific to the profile type and user;
- Device opens the profile in system preferences app and prompts user to install the received profile.
A minimal configuration profile to add a bookmark to this fine blog looks like this (you can download and hack around with a full example from GitHub):
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>PayloadIdentifier</key> <string>com.fullcontact</string> <key>PayloadType</key> <string>Configuration</string> <key>PayloadUUID</key> <string>f6c24163-66fb-4a42-a625-d8f22d23f371</string> <key>PayloadVersion</key> <integer>1</integer> <key>PayloadContent</key> <array> <dict> <key>PayloadType</key> <string>com.apple.webClip.managed</string> <key>PayloadUUID</key> <string>f78c5002-3907-4f67-b631-d41c44283628</string> <key>PayloadVersion</key> <integer>1</integer> <key>URL</key> <string>https://www.fullcontact.com/developers</string> <key>Label</key> <string>FullContact Blog</string> <key>PayloadIdentifier</key> <string>com.fullcontact.blog</string> </dict> </array> </dict> </plist>
A couple of points to note here:
- The actual data about the new bookmark being added is contained in the
PayloadContentarray. This means that a single configuration profile can be used to add multiple settings with a single profile.
- Keys common to all types of config payloads must be present in both the top level of the payload and in the section about each individual config type.
- Fields like
PayloadTypework only with a limited set of values. If payload version is other than “1” and payload type is not listed in the Apple docs, the installation will fail.
Now that the basic concepts have been covered, we arrive at CardDAV. CardDAV is an open source protocol for contact data exchange on the web. When you are using contact management apps that support Google Contacts or iCloud, you might be using CardDAV for contact sync under the hood.
For users, the problem is that installing CardDAV is anything but easy. To add a CardDAV source to an Apple contacts app, the user needs to provide three things – a valid CardDAV server URL, username, and password. And before that, the user needs to find a place where a CardDAV source can be added (and for developers and product teams, explaining contact sources and CardDAV to users might be too hard of a task in some cases). If a service provides CardDAV capabilities without automated installation, the user must be really motivated to strike out and find how to get the service working.
Which brings us back to configuration profiles. When using configuration profiles your users can perform a completely automated install. All they need to do is accept the installation. There is no need to look up service addresses or re-auth in a service they might already be using on their device.
Configuration payload for installing CardDAV is no different from the bookmark payload. The only change is in the
PayloadContent array. It has to contain a CardDAV-specific content (among other possible configurations).
<dict> <key>PayloadType</key> <string>com.apple.carddav.account</string> <key>PayloadUUID</key> <string>f78c5002-3907-4f67-b631-d41c44283628</string> <key>CardDAVUsername</key> <integer>user</integer> <key>CardDAVPassword</key> <string>foo</string> <key>CardDAVHostname</key> <string>example.com/carddav</string> <key>PayloadIdentifier</key> <string>com.fullcontact.carddav</string> </dict>
And that’s it! With a single tap the contact sync process will start in seconds.
In short, using a configuration profile provides a streamlined user experience. Users might even not notice that this is CardDAV, as contacts synced via installed source will be displayed together with all other contacts on the user’s device. It’s that simple.
Naturally, there are a couple of security issues related to configuration profile installation which are specific to iOS devices.
When starting the profile installation process from an iOS app, additional HTTP headers cannot be added to the request. Because of this, any pieces of data identifying the user that is about to install the profile (auth tokens, session ids) must be sent as a part of the request query string. Which means the profile installation URI (with the possibly sensitive user data) remains in the Safari for iOS address bar.
If the profile installation can be repeated, then all sensitive data (user identifier and contents of the XML payload) can leak. Obviously, this is problematic.
To solve the issue, the best security measure is to use auth tokens that expire (or invalidate) after using and generating a unique password for users that is utilized only in CardDAV.
On a Mac, it’s a much easier process: if profile installation is started from a Mac app, the profile installation opens System Preferences directly and urls are not exposed to users.
While we were working on the CardDAV profile installer, we discovered a couple of caveats and inconsistencies. None of these have been documented in the official docs, so there might be similar issues with other types of configuration profiles (WiFi, VPN management) as well. The most painful were:
- Apple contacts apps try to use HTTPS by default, so values for using SSL and setting the port can be left out. If added, the CardDAV profile installation won’t work on Mac.
- CardDAV has a concept of principal URL, which contains information about address books for a user. Principal URL can be set in the configuration profile, but the set value is not being used (the actual principal URL is sent to the client in the CardDAV auth process instead).
- As the auth process may be async and happen only when the contacts app is opened, the user won’t be prompted to enter a password in the configuration when the credentials in the configuration payload are incorrect.
Another potential pitfall is signing.
I’ve mentioned that the plist payloads need to be signed. In the bookmark example, I demonstrated how to sign the payload with a self-issued certificate. Although possible, using this method will look sketchy enough to System Preferences apps that they will warn users with an ugly notice:
To fix this, XML payloads need to be signed with verified SSL certificates. If your website (foobar.com) offers verified HTTPS connection and you are distributing configuration profiles through the foobar.com domain as well, you can simply reuse the certificate for signing XML payloads. If you have your certificates in PEM format, nothing really changes in the code (the example above uses self-signed certificates), but users get a nice “verified” sign when installing.
This post uses CardDAV to illustrate the power of configuration profiles. But possibilities of configuration profiles cover much, much more.
If you are building a client-server app that is targeted at Apple devices, I really encourage you to go and check out the lone manual on this topic to see the complete possibility list.