OAuth 2.0 with Swift Tutorial

In this OAuth 2.0 Swift tutorial you will learn how to use two different open source libraries to implement OAuth 2.0 in an iOS app. By Owen L Brown.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Using Embedded Safari View

Embedded Safari web views make for a more user-friendly experience. This can be achieved by using a SFSafariViewController rather than switching to the Safari app. From a security point of view, it’s a less-secure approach since your app’s code sits between the login form and the provider. Your app could use Javascript to access the credentials of the user as they type them. However, this could be an acceptable option if your end users trust your app to be secure.

oauth2-explained-2

You’ll revisit the share method using the OAuthSwift library, but this time, you’ll implement OAuth 2.0 using an embedded Safari view.

OAuthSwift with Embedded Safari View

You’re going to start again with a different project. So close the existing Xcode workspace, download this version of the Incognito starter project, and open the project in Xcode using the Incognito.xcworkspace file.

Build and run the project; things should look pretty familiar.

As before, you first need to import the OAuthSwift library included in the project.

Open ViewController.swift and add the following import to the top of the file:

import OAuthSwift

Still in ViewController.swift, add the following code to share():

//1
let oauthswift = OAuth2Swift(
  consumerKey:    "YOUR_GOOGLE_DRIVE_CLIENT_ID",         
  consumerSecret: "",		// No secret required
  authorizeUrl:   "https://accounts.google.com/o/oauth2/auth",
  accessTokenUrl: "https://accounts.google.com/o/oauth2/token",
  responseType:   "code"
)

oauthswift.allowMissingStateCheck = true
//2
oauthswift.authorizeURLHandler = SafariURLHandler(viewController: self, oauthSwift: oauthswift)

guard let rwURL = URL(string: "com.raywenderlich.Incognito:/oauth2Callback") else { return }

//3
oauthswift.authorize(withCallbackURL: rwURL, scope: "https://www.googleapis.com/auth/drive", state: "", success: { 
  (credential, response, parameters) in
  oauthswift.client.postImage("https://www.googleapis.com/upload/drive/v2/files", 
    parameters: parameters, 
    image: self.snapshot(), 
    success: { 
      //4
      (response) in
      if let _ = try? JSONSerialization.jsonObject(with: response.data, options: []) {
        self.presentAlert("Success", message: "Successfully uploaded!")
      }
    }, 
    failure: { 
      (error) in
      self.presentAlert("Error", message: error.localizedDescription)
    })
}, failure: { (error) in
  self.presentAlert("Error", message: error.localizedDescription)
})

Here’s what’s going on in the code above:

  1. You first create the OAuth2Swift that will handle the OAuth dance for you. Don’t forget to replace YOUR_GOOGLE_CLIENT_ID with the client id from the Google console.
  2. Then initiatize the authorizeURLHandler to a SafariURLHandler which will automatically handle displaying and dismissing a SFSafariViewController.
  3. Next, request authorization via the oauthswift instance. The scope parameter indicates that you are requesting access to the Drive API.
  4. If authorization is granted, you can go ahead and upload the image.

Configuring URL Handling

Just as in the previous project, this version of Incognito has been set up to accept a custom URL scheme; all you need to do is implement the code to handle the custom URL.

Open AppDelegate.swift and add the following import:

import OAuthSwift

Then, implement application(_:open:options) as shown below:

func application(_ app: UIApplication,
                 open url: URL,
                 options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
  
  OAuthSwift.handle(url: url)
  return true
}

Unlike AeroGearOAuth2, OAuthSwift uses a class method to handle parsing the returned URL. However, if you inspect the handle(_) method, you’ll see that it simply sends a Notification, just like AeroGearOAuth2 required you to do!

Build and run your project; note that when the authentication form appears, it’s not displayed within the Safari app, and no app switching happens. As well, the authentication form is presented each time you run the app since no web cookies are stored in your app by default.

Using a SFSafariViewController to authenticate with Google looks more streamlined, for sure! :]

You can download the final Incognito OAuthSwift project here.

More About Tokens

One thing you haven’t looked at is how to store those precious access and refresh tokens which you receive as part of the OAuth 2.0 dance. Where do you store them? How do you refresh an expired access token? Can you revoke your grants?

Storing tokens

The best way to store them is…on your Keychain, of course! :]

oauth2-epalined-4b

This is the default strategy adopted by OAuth2Session (from AeroGear).

If you would like to read more about the keychain, then I recommend reading our other tutorials on the subject.

Refreshing and Revoking

To refresh the access token, you simply make an HTTP call to an access token endpoint and pass the refresh token as parameter.

For example, AeroGear leaves it up to the library to determine whether the token is still valid.

OAuth 2.0 defines a different specification for revoking tokens, which makes it possible to either revoke tokens separately or all at once. Most providers revoke both access and refresh tokens at the same time.

Where To Go From Here?

You covered two open source libraries which implement OAuth 2.0 – and hopefully learned a little more about how OAuth 2.0 works under the hood.

Maybe now you’re ready to read the OAuth 2.0 specification, RFC6749?! OK, maybe not. It’s a beast of a document! But at least you now understand the fundamentals and how it relates to your app.

I hope you use one of them in your app. Once you’ve picked your favorite open source OAuth 2.0 library, contributing to it is essential. If you notice a bug, report an issue. If you know how to fix it, even better – propose a pull request.

If you have any comments or questions about this tutorial, please join the forum discussion below!