One of the challenges faced with developing in-house (or “enterprise”) mobile applications is deployment. For public applications, deployment is handled almost exclusively through platform app stores, like the iOS App Store or the Google Play Store. Deploying apps to employees of a single company, without making use of those stores, is a difficult problem. Android apps can be deployed just by making the apk available, but browsing, versioning and updating is still a problem.
Many companies solve this problem through the use of a Mobile Device Management (MDM) server that includes app catalog or deployment capabilities. Good Mobile Manager and AirWatch are both popular and (according to Gartner) leading platforms for MDM. My company tried out AirWatch, but ultimately decided that it was a bit too buggy and high maintenance, without much benefit.
Without going too much off topic, here’s a brief overview of what AirWatch was, and wasn’t, for us. MDM servers often provide the ability to remotely encrypt and wipe devices, install or uninstall enterprise apps, enforce usage and security policies, and other things. AirWatch did none of these things well, and some didn’t work at all due to platform limitations *AHEM*android*AHEM*. What we really needed was a way for employees to have access to our in-house developed applications. We already had some security control via our exchange server and it’s policies, and we could enforce other levels of security through the apps themselves (i.e., verifying that the device is encrypted, or has a passcode set).
I designed, proposed, and implemented a custom enterprise app catalog for my company to solve this problem. This app catalog would meet our greatest need by enabling users to download our in-house applications. It also provides us with some statistical information through basic analytics (a combination of Google Analytics and my own user-agent tracking). It also provides us with security of downloads, by authenticating users and download requests against active directory. It does not provide any remote installation/updating/uninstallation, nor any remote wiping capabilities. Some of the other features include:
Automatic PLIST generation for iOS over-the-air installation
Token based authentication for iOS over-the-air installation (which doesn’t support authentication cookies)
Role based app availability (beta testers, for example)
A management UI for adding or editing users and roles
A management UI for adding and editing apps
Automatic “compatible app” views based on detection of device platform and version
Mobile optimization including standalone “web app” mode on iOS
The app catalog I built (cleverly named “App Catalog”) is built with ASP.NET MVC 3, SQL Server, Entity Framework 4, and jQuery Mobile. Honestly, I used Entity Framework because I wanted to try it out. It ended up being extremely useful, combined with linq-to-entities. I can let my entity data model diagram speak for itself about my design.
Authentication and Downloading
Unauthenticated users are redirected to a login page, which accepts domain credentials and authenticates against active-directory using forms authentication. I leverage forms authentication cookies to allow users to remain signed in for 14 days.
To explain the authentication of downloads on iOS, I need to take a brief tangent here and explain how iOS OTA installation works. For more information, check out this blog post. To trigger an iOS OTA download, the link must be formatted with a special
itms-services:// protocol link. When clicked, it first requests a plist file (which must be returned with the mimetype
text/xml) that includes information like the app package name, version, icon url, package url, and a few other various items (this plist file can be generated by Xcode by selecting “distrubute for enterprise” when archiving an application). Then, it uses the package url to download and install the application package. All of this is invisible to the user except for the initial “foo.com would like to install ‘Bar.’ would you like to continue?” message (which occurs between the plist and package (ipa) downloads). Unfortunately, this double-bounce OTA installation does not support cookies, or at least it does not use Safari’s cookies.
Because of this, if the plist or ipa files are behind an authentication wall, the download will fail. In order to authenticate the downloads, when the link to the plist is output on the app catalog page, it is a link to an unsecured action on my controller, with the current authenticated user’s unique download token as a parameter. Then, when the action is requested, that token is authenticated and the plist file is generated from a template (this killed two birds with one stone – authenticating the plist request, while making the plist dynamically generated. Managing the plists manually is a pain, especially with the weird caching that the OTA service does). The proper icon link, file link, app name, and package name are filled in, and returned as a plist file. The package link works the same way – it’s a link to a file download action, with the token as a parameter. When the request comes to this action, the token is verified again, and the app package file is returned (with mimetype
It’s important that we go through these steps to authenticate the plist and ipa files, because our Apple Enterprise Developer license only allows employees of the company to download applications signed with our certificate. If our apps were to be vulnerable to anonymous downloads, our certificate and license could be revoked. Much of this is specific to the iOS OTA installation; for Android, the download link points to the app package download action which authenticates the token and returns the app package directly.
When a user first arrives at the site, they see our company’s in-house apps that are compatible with their device. This compatibility check is done in the request. A request’s user agent is parsed for platform (Android, iOS, Windows) and version, and I use linq-to-entities to get all apps that match the platform and are compatible with their version or lower. Other views allow viewing all apps, or public apps (like recommended applications in public app stores). The apps are presented in a mobile-friendly jQuery Mobile listview with icons, titles, and descriptions, and two-tap downloads (one to confirm).
Because we can check for Windows, we also have some listings for common Windows applications that our teams use, and we have a special page for Windows software stored on our network, which simply displays the path to find it on the network, as opposed to a download or installation link.
iOS Supports standalone web applications and webclip (shortcut) icons, so all of these are configured. This provides a more streamlined feel to the website, almost as if it’s an installed application.
I put quite a bit of effort into the management UI for the app catalog. Naturally, we needed the ability to quickly create and update applications in the catalog, without manually copying files and editing the database. There’s an editor form for uploading new icons and app package files, as well as changing any of the properties of the apps, including availability, version, name, description, and platform/platform compatibility. Likewise, there’s a user manager page, where users can be assigned to roles like “tester” or “admin.” All of these management options allow extremely low overhead for updating applications. We even have a module for our apps that checks the app catalog with it’s catalog Id for newer versions, so when a new version goes up, the user is notified right away.
I'm sure there are some features that I've missed, or features that are purposely missing that would be deal breakers for others who might need an app deployment solution. For now, it's been working pretty well. I've only had one logged error in the last 3 months, and we've hit 200 downloads (300 if you include all of our testers' downloads) of apps in our company, from the app catalog. I've very open to feedback and questions!