www.robkerr.com
www.robkerr.com

mobile development, cloud computing and building great software

Rob Kerr
Author

Addicted to coding since writing my first programs for the Commodore computers in the 1980s. Currently working as an independent contractor focused on native iOS development.

Share


Tags


Twitter


www.robkerr.com

User authentication using the UBER iOS API with Swift

Rob KerrRob Kerr

Many apps have a requirement to use authentication with 3rd party cloud providers. In this post I'll look at the reasons for integrating with the Uber SDK for user authentication, then we'll walk through a basic integration with the Login portion of the Rides API.

For full documentation of the Uber iOS API after reading this intro article, swing on over to https://developer.uber.com/docs.

Why use Uber authentication?

Why would we want to use Uber auth instead of (or in addition to) providing our own authentication database?

  1. Easier user experience. If all (or most) of our app's users already have Uber login IDs, it may make sense to allow the user to authenticate with Uber, and then connect their Uber ID with our own web service API's User database. This can be a better experience for the user, since they don't need to create and remember a custom user id/password assigned by our app, and they may be more willing to adopt our app if they don't have to go through the pain of establishing a new login Id and password with us.

  2. Easier and more secure for our app. Since we're effectively "outsourcing" identity to Uber, we don't need to manage/secure user passwords, implement password reset processes and so on.

  3. Uber Transactions on behalf of users. Uber's API allows not just user identification/authorization, but we can also use the user's token to do work on his or her behalf. For example, we might automatically schedule an Uber ride for the user -- either within the client app, or as part of a server API process.

Overall Process

Here's the overall process of integrating Uber Identity. If you read my similar post on using the Facebook API for user identity, this process will seem quite familiar.

  1. Register our app on Uber. This lets Uber know who we are, so when authentication requests come to their API our app will be trusted.

  2. Integrate the Uber API into our XCode project. Just like any integration, we'll need to add some frameworks. In this project I'll be using CocoaPods to download and install the Uber frameworks. You can also download and integrate manually, but CocoaPods is easier to establish and keep up-to-date as the API evolves over time.

  3. Add a button to the app UI to initiate the Uber login request. I'll use the manual method (in code) for this integration.

  4. Capture the Uber user ID, and insert it into our own web service database so we can associate the Uber users with one of our users. I won't go into detail on this step for this post, but once you have the data back from Uber, suffice it to say you can store it where you need it.

Register with Uber

As with many 3rd-party service APIs, the first step is to register with Uber as a developer, at https://developer.uber.com, and then register a new app.

Registering the app is straightforward. I'll skip the preliminaries and go right to discussing the meat of the integration:

Basic app description and authentication keys

Uber App Screen1

On the first page of the app registration page, Add a display name for your app (Name), and a longer description of what your app does (Description).

Like most API services, you'll have a Client ID, Client Secret and Server Token, which are used by your app to provide app-to-app authorization with the API. Make a note of these values, which you'll need in your code. You won't use all of these values in the iOS app. Some of these various settings are used in web server to web server apps as well. I'll cover only the settings relevant to iOS here.

App URLs and bundle IDs

The second page of the app registration asks for redirect URLs, which are standard OAuth2 conventions. You'll add two for an iOS app.

Below the URLs are fields to capture your iOS bundle IDs (these are labeled "app signatures"). You can add more than one bundle ID, which would support multiple iOS apps with one Uber app definition.

Uber App Screen1

For your iOS application, the Redirect URLs can be virtually anything. Uber won't really be redirecting to your app with a web browser URL, but it is important to use the same string here on the developer dashboard as you enter in your iOS app's info.plist later on.

You should have two Redirect URLs. There may be more, for example to support Android or Web applications with the same Uber App.

The URLs for the iOS app have a convention to signal classifications of commands. For example:

MyApp://UberGeneral
MyApp://UberAuthorizationCode

Here the first is the URL used to signal a "general" API command, and the second to exchange authorization codes. This may be confusing now, but as the info.plist is configured below, it will become more clear.

The App Signatures register your iOS bundle ID. When the API embedded in your iOS app makes a call to the web service, it will pass your bundle id automatically, and Uber will use it as part of the authorization process.

When these fields are filled and saved, you've done enough to connect your iOS app with Uber, and can move on to the next step.

Adding the Uber API to our XCode Project

Next up, we need to add the Facebook API to our XCode project. This should be done by using CocoaPods to install the Uber SDK into the project.

Install CocoaPods

If you're not familiar with CocoaPods, it's a Ruby-based package manager that modifies your XCode project to include 3rd-party modules (like the Uber API). CocoaPods will configure an XCode workspace that includes your project, and additional projects for the 3rd-party components.

If you don't have CocoaPods installed on your development workstation, or don't know what it is, browse over to https://cocoapods.org to learn about it and install it before continuing.

Create the Podfile

After creating your base XCode project, open a bash shell in the same folder where your .xcodeproj is located, and then run the following command to create a new Podfile

$ pod init

Next edit the Podfile using the text editor of your choice. Update the new Podfile to look something like the following. Instead of 'MyAppTargetName', specify the base filename of your .xcodeproj file.

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.2'

use_frameworks!

target 'MyAppTargetName' do
    pod 'UberRides'
end

After updating the Podfile, make sure XCode is closed, and then run a the following command to download the Uber frameworks and configure your XCode workspace:

$ pod install

After this step succeeds, you should never have a reason to open your .xcodeproj project directly. From now on always open the .xcworkspace file instead. The Uber API framework will be a new project in the workspace, referenced by your original project when editing and building the project.

If Xcode is open, close it. Then double-click on the .xcodeworkspace file to open the updated project workspace.

Update the info.plist

As with most OAuth-style integrations, there's some info.plist work to be done. Open the App's info.plist and first add the following settings within the outer <dict> tag. These specific settings are required in apps targeting iOS 9 or later as part of the declarative network protocol authorization approach Apple added in that version.

<key>LSApplicationQueriesSchemes</key>
<array>                                           
    <string>uber</string>
    <string>uberauth</string>
</array>

These settings are static, and don't change depending on your specific application.

Next, add the following settings within the outer <dict> tag, which are specific to your application. Note the specific settings, which are the ones you either entered previously in the Uber app registration, or obtained from that same app registration page.

The strings that are specific to your app are indicated by (1), (2), etc., and are explained below.

<key>UberCallbackURIs</key>
<array>
    <dict>
        <key>URIString</key>
    <string>***** (1) *****</string>

        <key>UberCallbackURIType</key>
        <string>General</string>
    </dict>
<dict>
        <key>URIString</key>
        <string>******** (2) ***********</string>
        <key>UberCallbackURIType</key>
        <string>AuthorizationCode</string>
    </dict>
</array>

<key>UberClientID</key>
<string>****** (3) *********</string>

<key>UberDisplayName</key>
<string>******** (4) ********</string>

Items (1) and (2) are the redirect URLs that you entered into the Uber app dashboard as REDIRECT URL entries.

The UberCallbackURIType is important, as it tells the API which string we want to be sent during an Authentication operation (AuthorizationCode) vs. during a general API operation (General). This is mostly under the covers configuration, but we do need to match these values correctly between the Uber Dashboard and info.plist.

Item (3) is the ClientID from the Uber app dashboard. For this entry, just copy the value from the dashboard (assigned by Uber) and paste it into he info.plist here.

Item (4) is as it sounds -- the name the user will see. Match this to the App Name you entered on the Uber Developer dashboard.

Implement AppDelegate Methods

As the integration with the Uber API flows, our app will be launched with Uber keys and callback information. We need our AppDelegate to then pass this information on to the Uber API so that API can process it on the client end properly.

First, add the Uber Ride API import to the top of AppDelegate.swift

import UberRides

Next, implement the application - openURL method in the AppDelegate

func application(_ app: UIApplication, 
         open url: URL, 
         options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    let sourceApplication: String? = 
         options[UIApplicationOpenURLOptionsKey.sourceApplication] as? String
    return  RidesAppDelegate.sharedInstance.application(app, open: url, sourceApplication: sourceApplication, annotation: nil)
}

Note I'm using Swift 3.0 syntax here, so if you're using an older version of Swift, adjust as needed.

Implement the User Interface

Since this is a learning exercise, I've implemented a simple UI that just has an Uber login button placed on it.

When the user presses the button, the Uber SDK uses the information in info.plist, combined with the configuration made in the viewDidLoad() method to call out to Uber with the Client ID and bundle ID.

Uber then takes over, presenting UI for the user to login. When the user logs in (or, possibly, cancels), control returns to our app via the AppDelegate methods implemented earlier.

Uber Storyboard

In the storyboard I've placed a UIView into the view hierarchy which is connected to the outlet logoutButtonBgView. Then, in code, I programmatically create the actual button and embed it in this button container with the same bounds.

Implement the ViewController integration

Now with the integration completed in info.plist and AppDelegate, and with the UI laid out in the Storyboard, we can finish the integration by implementing the button code in the ViewController.

When thinking about the authentication done with the Uber API, really what we're doing is enabling the button. So just keep in mind that the button does the work, and we just configure it and respond to it.

First, add the Uber Ride API import to the top of ViewController.swift

import UberRides

Then, at class scope, define some data structures used to configure the Uber Login button, and an outlet for the logoutButtonBgView created in the Storyboard.

@IBOutlet weak var logoutButtonBgView: UIView!

var uberScopes: [RidesScope]?
var uberLoginManager: LoginManager?
var uberLoginButton: LoginButton?

Next, declare the ViewController as a delegate that implements the LoginButtonDelegate protocol. I think it's unfortunate that Uber didn't make it clear that this protocol is associated with Uber, and I hope in a future version they will call it something like UberLoginButtonDelegate. But as of this writing, it's LoginButtonDelegate

public class ViewController: LoginButtonDelegate {

Within viewDidLoad(), add the following code to configure the Uber API environment, letting the API know how we intend to have the user login (using the Uber App installed on the iOS device), and what features we intend to use (user profile)

uberScopes = [.Profile]
uberLoginManager = LoginManager(loginType: .Native) 

Then, also in viewDidLoad(), add code to create the button and embed it inside a UIView placed on the form called `uberButtonBgView'.

if let loginManager = uberLoginManager, let scopes = uberScopes {
    // create a button at runtime
    uberLoginButton = LoginButton(
           frame: CGRectZero, 
           scopes: scopes, 
           loginManager: loginManager)

    // if created, configure the button's behavior
    if let button = uberLoginButton {
            button.presentingViewController = self
            button.delegate = self
            button.frame = uberButtonBgView.bounds
            button.autoresizingMask = 
                       [.FlexibleWidth, .FlexibleHeight]

            uberButtonBgView.addSubview(button)
    }
}

Finally, implement the LoginButtonDelegate method that's called back by the Uber API when the user successfully logs into the application

func loginButton(button: LoginButton, 
              didCompleteLoginWithToken accessToken: AccessToken?, 
              error: NSError?) {

    if let token = accessToken {
        if let tokenString = token.tokenString, 
           let refreshToken = token.refreshToken, 
           let expirationDate = token.expirationDate {

                 // Send token data to the Web API
        } // unwrap token member objects
    } // unwrap accessToken
}

Conclusion

And that's pretty much it. At this point we've integrated our app with Uber so that our users authenticate their identity with Uber via the Uber iOS API, and we can use the Uber user ID to associate all the rest of the data in our custom app with Uber.

Where to Next?

If your intent with Uber is to simply use it as an identity provider -- well we're pretty much done! However, there's more that can be done with the Uber API.

You might have a destination calculated by your app (or web API), and once logged in, you can use other Rides API calls to have Uber start a ride request--saving the user from re-entering that data again into the Uber app.

Or, if you really want to build a complete end-to-end solution in your own app, you actually can! You could completely schedule Uber rides or other operations within your custom app.

Rob Kerr
Author

Rob Kerr

Addicted to coding since writing my first programs for the Commodore computers in the 1980s. Currently working as an independent contractor focused on native iOS development.

Comments