loading...

Quick Guide to ASWebAuthenticationSession API Changes in iOS 13

robotsquidward profile image A.J. Kueterman ・4 min read

One of the biggest announcements at WWDC 2019 was the new 'Sign In With Apple' feature, where Apple will now provide an authentication email & password to apps on behalf of you and manage them securely using the iCloud Keychain.

sign-in-with-apple

Apple continues to push these privacy-focused features especially around authentication to try to disrupt ubiquitous services like Facebook & Google OAuth that make login flows much easier for users - but at the cost of their data going into the hands of ad companies.

As such, a lot of focus has been given to the Authentication services in iOS. We saw it begin last year with the move from auth services inside SafariServices to the dedicated AuthenticationServices APIs. This year, iOS 13 leverages AuthenticationServices to enable the Sign In With Apple APIs, and continue to refine the sign in experience.

A Guide to Changes in ASWebAuthenticationSession

Last year, I talked a bit about OAuth in iOS in my post about Apple's move from SFAuthenticationSession to ASWebAuthenticationSession and how I went about converting my iOS app to use the new API. This year, the ASWebAuthenticationSession has some smaller tweaks to enhance the OAuth sign-in experience across iOS (including iPadOS) devices.

If you're a developer already sweating about the huge array of changes and opportunities coming for developers in 2019, don't fret about this one, it's a small tweak purely for enhancing OAuth experiences across devices.

To set the stage, let's look at a code snippet for ASWebAuthenticationSession in iOS 12.

//...
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()
}

In this example, the only thing we need to provide the ASWebAuthenticationSession is the authentication URL, a callback URL scheme, and a completion block that handles the result of the OAuth. The OS handles the rest - displaying an alert, launching a Web login flow, and dismissing.

In iOS 13, as Apple continues to refine the multi-app experience for iOS and iPadOS, we now need to help the OS out when it's making the decision on where and how to display the OAuth Alert and Web login flow.

To do that, we have to let the ASWebAuthenticationSession know which window is presenting the OAuth request. This is done by implementing the ASWebAuthenticationPresentationContextProviding interface in your presenting View Controller.

The presenting View Controller needs to implement the ASWebAuthenticationPresentationContextProviding interface and return the relevant window in the presentationAnchor method.

class LoginViewController: UIViewController, ASWebAuthenticationPresentationContextProviding {
    //...
    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        return self.view.window ?? ASPresentationAnchor()
    }
    //...
}

Then, when setting up our auth session, we need to specify our presentationContextProvider delegate.

self.webAuthSession?.presentationContextProvider = context

The full updated method, now passing in a ASWebAuthenticationPresentationContextProviding context (our presenting VC that implements the ASWebAuthenticationPresentationContextProviding interface).

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

    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")
    })

    // New in iOS 13
    self.webAuthSession?.presentationContextProvider = context

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

At this point, your ASWebAuthenticationSession now knows where and how to display your Web-based login flow, and will provide a consistent experience across devices!

Thoughts on OAuth

It has been an interesting experience tinkering with OAuth over the last two years. Right as I was learning the APIs Apple made a big move to new APIs, and this year it's clear how that change is enabling a better sign in experience for users.

Now, in iOS 13, by observing the specific change in this small API, we can see the evolution of iOS to a multi-window experience.

As I see this slow progression play out, I'm beginning to wonder about the future of OAuth in iOS at all. Will web authentication be blacklisted altogether in favor of Sign In With Apple? Will Apple centralize auth in a way that allows me to authenticate with GitHub without going to GitHub services for tokens? Only time will tell, but the changes today can project the changes in the future - stay alert!

If you have any thoughts/questions/predictions about OAuth and Web Authentication with iOS, don't hesitate to reach out to me on Twitter @ajkueterman.

Posted on by:

robotsquidward profile

A.J. Kueterman

@robotsquidward

Mobile developer with a passion for design.

Discussion

markdown guide
 

i have a question, i auth my user with AuthentificationServices but at disconnect, i don't know how to flush the session previously saved, if a user clic on reconnect right back, it will be connected with the previous account that made the disconnection
do you have an idea ?

 

ASWebAuthenticationSession doesn't maintain your session itself, but it does use session stored in Safari. See this section in the docs:

If the user has already logged into the web service in Safari or other apps using ASWebAuthenticationSession, it’s possible to share the existing login information. The system presents the user with a dialog asking for consent to share login information. If the user cancels the alert, the session is canceled, and the completion handler is called with the error code ASWebAuthenticationSessionError.Code.canceledLogin.

So if you're authenticating to an OAuth service/app and that service maintains a user's session in Safari then you might see it auto-authenticate you without having to re-enter a username/password. This is only an issue if you want to support multiple logins for a single device - more likely to be an issue during your testing than in normal use (in my experience).

iOS let's users opt-out of the auto-login using previous session each time it tries to authenticate them with the previous session, but I'm not sure of an easy way to flush their session on behalf of them every time you try to do an OAuth from your app.

Hope that helps!

 

Thank you for your kind and detailed answer,
Indeed, maybe my wish to fully disconnect after logout is related to my dev role and I would find this annoying as a user.
I’m still curious if there is a possibility or if it’s intended in the design of ASWebAuthenticationSession

You could try prefersEphemeralWebBrowserSession, a new feature in iOS 13, to at least block your web view from sharing session with Safari and be a bit more explicit about making your user login. I'm not sure it speaks to your specific issue, but might be worth exploring.

 

Ran into the same issue, what we ended up doing, was instead launching a clear session url which auto redirects to the login each time.

 

I am trying to use the Ecobee API, but they want you to use a HTTPS prefixed URL for the callback scheme... "example.com:/auth".
When I try to get the auth token I can login and authorize my app, but it always ends with "cannot find the server..." and all I can do is cancel.

 

On GitHub (for using the GitHub API) you create an OAuth App that will accept the callback URL scheme and act as your auth server, by handling callbacks and returning a token for access. That's what I did for my implementation. See if Ecobee has a similar setup, otherwise you might have to implement your own auth server.

 

I tried ASWebAuthenticationSession mentioned in the above example, the callback handler is not being called. Hence, the web view doesn't dismiss after the authentication is complete.
The same thing I tried with WKWebView. It works fine.
Could you please help me with this?

 

If you're being authenticated but not routed back it sounds like it could be an issue with the callback URL? Just an initial thought, make sure your callbackUrlScheme is correct.

 

Hi,
Is there a way to set the modalpresentationstyle to Fullscreen for the aswebauthenticationsession controller?

 

There might be a way to enforce that for your entire application by writing an extension or swizzling, but I don't think you can set it on the ASWebAuthenticationSession provided modal. Hope that helps.

 

Same here, I'm having issues getting an auth code for the callbackURL. The crazy thing is that the ASWebAuthSession's callback doesn't execute. I've given it a valid url and callbackURLScheme.

 

Hi, I'm having trouble to get the call back in completion handler once response back from the API. can you help me pls?

 

@ajkueterman,

i have implemented the ASWebAuthenticationSession as you said in example but somehow its not working, could you please share the sample project to login OAuth.

Thanks in advance.

 

Unfortunately I don't have any open source apps that are using the modern ASWebAuthenticationSession flow. I'll see if I can't put something together soon, but for now if you have specific questions I can try to help.

 

Hi, as my app has different points of access to login, is it safe to implement ASWebAuthenticationPresentationContextProviding in the Application Delegate instead of a UIViewController?