Authentication in iOS 11
A look into the background and purpose of Apple’s latest update to the Safari Services Framework
There is no ‘history book’ for legacy code — no centralized place to look back at several generations of implementations and appreciate the simplicity and expressiveness of modern frameworks. However, the more logic that gets abstracted into frameworks, the harder it becomes to understand the impact of a few lines of code. It is essential for developers to understand why software is built as it is, its origins and even its previous failures.
The recent developments of web service login on iOS are an interesting progression of Apple’s frameworks and provide an great case study of framework evolution. This post is intended to give a brief historical and technical analysis of web service authentication in iOS apps.
OAuth 2.0, a widely adopted protocol known for powering the “Login with Facebook” button, was created in 2012 — the same year that iOS 6 was released. Although it took a while, Apple finally began supporting this modern standard with the Safari Services Framework — first in iOS 9 with SFSafariViewController (2015) and again in iOS 11 with SFAuthenticationSession.
When using an app that has an OAuth login, a user is temporarily redirected to a page (usually in a web view) controlled by the OAuth provider. Before Apple began supporting this process, users were forced to manually input their login information into the web view. Now, however, the Safari Services Framework lets users access their login credentials from other apps they are already logged into — eliminating the need for users to retype their password. This makes it easy for developers to remove a barrier to entry and improve the app’s conversion rate. By making features like authentication easier to build, Apple enables developers to make more compelling apps for the App Store.
While writing this post, I caught myself using the terms “authentication” and “authorization” interchangeably. Unsurprisingly, it turns out that they do not actually mean the same thing. Authentication “is the process of verifying who you are” whereas authorization “is the process of proving you have access to something.” Simply, your app must authenticate a user before authorizing that the user has access to a given resource. This post talks about the web service authentication processes that Apple supports through the Safari Services framework.
Note: While OAuth 2.0 is actually considered an authorization protocol, this post uses OAuth 2.0 as an ongoing example for its associated authentication scheme.(i.e. authentication that requires a user to authorize access to their Facebook profile)
A Brief History of iOS Authentication
At their best, frameworks can both expedite the development process and provide solid security and performance improvements. At their worst, frameworks make it easy to forget what’s actually going on under the hood — leaving some bugs nearly impossible to fix. Apple’s new SFAuthenticationSession is a positive change in the Safari Services Framework — one that improves the security of users while reducing the complexities that that developers face with authentication.
The Old Way
Before iOS 11, SFSafariViewController (released in iOS 9) was Apple’s primary solution to implement web service authentication. SFSafariViewController finally allowed access to cookies previously stored in Safari to gain authentication information. This allowed developers to bridge authentication sessions between web apps and corresponding iOS apps (and even to other third-party apps). Now, a user that is already logged into a website on Safari can login into its app with just the tap of a button — no password entry needed. This article has an excellent description of all that SFSafariViewController did for authentication. (Note: the last example from the linked article was invalidated by iOS 10: SFSafariViewController can no longer be presented while hidden.)
Although SFSafariViewController was a great step forward for displaying web content in an app, it forced an ugly implementation for web service authentication. This was a problem because web service authentication is one of SFSafariViewController’s main selling points. But since SFSafariViewController ultimately offered a better user experience, the ugly implementation prevailed.
Warning: iOS jargon approaching!
Implementing OAuth 2.0 before iOS 11 required the interception of a Safari redirect with the App Delegate’s (application: openURL: options:) method. From there, most apps completed the OAuth process by either posting a notification or by calling a method on an instance variable of the App Delegate — distributing the authentication code across multiple objects. This may not seem like a huge problem, but for medium and large-scale applications, this anti-pattern is confusing to follow and degrades the separation of layers in the application.
Additionally, many OAuth providers (like Facebook) only accept HTTP for redirect urls and do not accept the custom protocols that iOS apps use for deep links (appName://). This is an annoying constraint for iOS developers because the only way to implement SFSafariViewController authentication is by redirecting to the app’s deep link URL. This means you must first redirect to your custom backend (if you have one), which then redirects to your custom app URL.
To set up a deep link URL, you must configure the URL type in your project target (ProjectName > Info > URL Types) OR add a URL type to your Info.plist (like this).
Web service authentication before iOS 11 was scatter-brained. For such a common functionality, it’s frustrating that login is taking so long to get right. However, after considering the rise of OAuth 2.0, the privacy concerns of the public and the evolution of apps in general, it’s no surprise it has taken Apple a few years to catch up. Although the new API is not perfect, Apple’s new SFAuthenticationSession cleans up a key application design issue that was imposed by SFSafariViewController:
With just the short chunk of code above, all you have to do is add in deep link support in your project settings, and voila — authentication setup complete! What’s really cool about SFAuthenticationSession is you can add all of your login logic in the block immediately following the authentication session call (instead of intercepting the openURL method call in your app delegate like in iOS 9–10). Unfortunately, SFAuthenticationSession still forces the use of a custom app URL for the redirect — meaning that you still need a custom backend that can redirect the initial OAuth redirect.
This is inconvenient for applications that have no other need for a backend, but many apps that require web service authentication would benefit by moving their business logic to a server (e.g. smaller app archive size, the ability to manipulate logic without re-shipping, etc.). Apps that do not need or have access to a custom web service should consider alternatives to SFAuthenticationSession for their login implementation (like AccountKit by Facebook).
Overall, SFAuthenticationSession is a welcome change to the Safari Services Framework that provides a straightforward interface for a developer to implement authentication more securely. A future interface for iOS web service login could involve the acceptance of HTTP redirects in SFAuthenticationSession, or even an integration with pre-existing Universal Links configurations.
Special thanks to Santiago Gepigon III for reading drafts of this post