Web Authentication with iOS AuthenticationServices

Last week I started looking into doing OAuth for an iOS application to enable a simple GitHub authentication flow. After my quick research, I discovered there are more than a couple ways to do this authentication, but the newest and easiest way in Swift was the SFAuthenticationSession that was just introduced in iOS 11.

SFAuthenticationSession offers an easy API to launch a Safari View Controller to a given sign-in url where the user can authenticate through the web. When the OAuth service returns with the auth token, a simple completion handler is called to handle the response. It’s simple, secure, and really fast to implement.

Here’s my func to authenticate with my GitHub app OctoNotes in Swift:

//...
var authSession: SFAuthenticationSession?
//...
func getAuthToken() {
    //OAuth Provider URL
    let authURL = URL(string: "https://github.com/login/oauth/authorize?client_id=<client_id>")
    let callbackUrlScheme = "octonotes://auth"

    //Initialize auth session
    self.authSession = SFAuthenticationSession.init(url: authURL!, callbackURLScheme: callbackUrlScheme,
                                                    completionHandler: { (callBack:URL?, error:Error?) in

        // handle auth response
        guard error == nil, let successURL = callBack else {
            return
        }

        let oauthToken = NSURLComponents(string: (successURL.absoluteString))?.queryItems?.filter({$0.name == "code"}).first

        // Do what you now that you've got the token, or use the callBack URL
        print(oauthToken ?? "No OAuth Token")
    })

    //Kick it off
    self.authSession?.start()
}

Easy enough! An alert gets displayed to the user prompting them to auth, Safari handles it with their input/with the data it already has, then returns you an authenticated token.

But you’ll probably guess if you looked at that link to the SFAuthenticationSession docs that this isn’t the end of the story.

ASWebAuthenticationSession

On Tuesday at WWDC, in the “Automatic Strong Passwords and Security Code AutoFill” session, Apple announced a new API for authenticating through the web in your iOS apps, and deprecated SFAuthenticationSession. Short life for this handy API.

Not to fear! The replacement for SFAuthenticationSession is ASWebAuthenticationSession, and the new API is very familiar.

Instead of importing SafariServices, start by importing the new AuthenticationServices

import AuthenticationServices

Then follow the same exact pattern to start a ASWebAuthenticationSession and handle the callback to retrive your OAuth Token.

//...
var webAuthSession: ASWebAuthenticationSession?
//...
@available(iOS 12.0, *)
func getAuthTokenWithWebLogin() {

    let authURL = URL(string: "https://github.com/login/oauth/authorize?client_id=<client_id>")
    let callbackUrlScheme = "octonotes://auth"

    self.webAuthSession = ASWebAuthenticationSession.init(url: authURL!, callbackURLScheme: callbackUrlScheme, completionHandler: { (callBack:URL?, error:Error?) in

        // handle auth response
        guard error == nil, let successURL = callBack else {
            return
        }

        let oauthToken = NSURLComponents(string: (successURL.absoluteString))?.queryItems?.filter({$0.name == "code"}).first

        // Do what you now that you've got the token, or use the callBack URL
        print(oauthToken ?? "No OAuth Token")
    })
    
    // Kick it off
    self.webAuthSession?.start()
}

Familiar, eh? The new ASWebAuthenticationSession should enable you to securely authenticate on the web and future-proof your app for any security features involved in web-based login. So while we weep for year-long API’s, at least Apple is making the conversion a breeze.