Credentials-free authentication (part 2)

Paul Ardeleanu
Paul Ardeleanu’s Blog
7 min readNov 24, 2018

Showing a simple way to authenticate your users without exposing yourself to data breaches.

Part 2: The iOS app (native, using Swift)

Table of Contents

I. Intro

1.1 The APIs
1.2 New iOS App

II. The Verify Service

2.1 Constants
2.2 VerifyServiceError
2.3 URLSession
2.4 Requesting a Verification
2.5 The Verification Check
2.6 Cancelling a verification
2.7 All together now

III. The Login Screen

3.1 Login Storyboard
3.2 The Router
3.3 Navigation using the Router
3.4 The Login Screen
3.5 Activity Indicator
3.6 Starting the Verification
3.7 Main Screen

I. Intro

In Part 1, we built a Vapor app that we deployed to Heroku. This was needed to secure the Nexmo credentials (and not include them inside the iOS app).

In this second part, we’re going to build an iOS app that will authenticate the user without storing their credentials. It will do this by checking the ownership of a phone number using the APIs we’ve build.

1.1 The APIs

Here is quick summary of the live APIs we built:

  1. Start verification request:
    URL: https://[appname].herokuapp.com/verify/request
    Method: POST
    Parameters: api_key, number
  2. Fulfil verification:
    URL: https://[appname].herokuapp.com/verify/check
    Method: POST
    Parameters: api_key, request_id, code
  3. Cancel verification:
    URL: https://[appname].herokuapp.com/verify/check
    Method: POST
    Parameters: api_key, request_id

1.2 New iOS App

We are building an app named “Notes” using the Single View App template:

II. The Verify Service

Let’s start by creating a simple utility class that will be responsible for the interactions with the server — we’ll name it VerifyService and create a singleton for it to avoid duplicates:

2.1 Constants

To store all the hardcoded values we’ll need, we’ll define the following constants:

  • UserDefault — for the default variable names (we’ll use them later)
  • Auth — for the length of an authenticated session
  • API — host and apiKey values

If you are unsure of why enums are used here, see this blogpost. TLDR: enums with no cases are perfect for namespaced constants.

2.2 VerifyServiceError

We’ll also add, at the top of the VerifyService.swift file, a VerifyServiceError enum that we’ll use to express errors:

2.3 URLSession

An URLSession is also required to ensure that all calls are being sent on a background thread — create a new Swift file named ApiSession.swift for it:

2.4 Requesting a Verification

Let’s go back to our VerifyService and continue building it by adding a couple of utility methods. They are going to used to create the URLRequests for each of the API calls.

Here is the request call:

2.5 The Verification Check

The verification check follows the same logic: we’re sending a request to /verify/check with the api_key, request_id and code as parameters:

2.6 Cancelling a verification

To cancel a verification in progress, we’ll need to send a request to /verify/cancel with the api_key and request_id as parameters:

2.7 All together now

To make sure we’re on the same page, a better indented, more legible and complete version the VerifyService.swift file is available at: https://gist.github.com/pardel/9ccf5c418d58257f3e2b30299cc5cc8e

III. The Login Screen

Now that we have the service the deals with the network calls to the APIs, let’s start building the interface of our app. This will consists of mainly 2 parts, one containing the authentication screen and, the other, the rest of the app.

I always find it easier to split these parts into 2 separate storyboards — we’ll use the one we already have (Main.storyboard) for the app and create a new one for the authentication.

3.1 Login Storyboard

We’ll therefore create a new storyboard named Login.storyboard. I also find it easier (and more enjoyable to use) when these app “parts” as arranged in groups:

Let’s start building the storyboard:

  • add a UIViewController from the Libray (it is now, in Xcode 10, launched from the toolbar)
  • embed the view controller into a navigation controller (from the menu: Editor > Embed in… > Navigation Controller
  • set the navigation controller as the entry point for the storyboard ( Is Initial View Controller checkbox in the Attributes Inspector)
  • add a second View Controller
  • add a “Show” segue from the first view controller to the second so it can be pushed onto the navigation stack — name the segue: “showCode”
  • create two Cocoa Touch Class files, subclasses of UIViewController, called LoginViewController.swift and CodeViewController.swift
  • assign LoginViewController class to the root (first) view controller and CodeViewController to the second one

This is how our storyboard looks now:

3.2 The Router

The flow between the authentication part and the main part will be handled by a rudimentary router. Let’s create a new Swift file named Router.swift and define a Route as a closure that takes no parameters and returns nothing:

We’ll also define a Router class as a singleton, since we’re going to only need one router:

Our router needs to be able to store routes and navigate to them; so, we’ll add a property routes that stores a dictionary of named routes as well as methods to add and navigate to a route:

Notice the the add and navigateTo methods are static; also,we are using a preconditionFailure for when a route is not found.

3.3 Navigation using the Router

Let’s now use our Router to in the AppDelegate and add routes for navigating to home, login and logout:

We’ll then call addRoutes in application:didFinishLaunchingWithOptions and navigate to either home or login depending on the current state:

We’ve used a property on the VerifyService, hasBeenVerified, defined as a check for the time when we last verified the user:

Everything should be operational now so let’s Build & Run; you’ll see an empty screen with a navigation bar at the top — our Login view controller. Let’s add some “color” to it!

3.4 The Login Screen

Our login screen should contain just one text field to ask the user for their number. Let’s add just that, as well as a button to start the verification:

We’ll connect the text field to an IBOutlet in LoginViewController and the button to an IBAction:

We’ve also added a title for the screen and set the phoneTextField to any value previously entered (and stored in UserDefaults). If phoneTextField is empty when the login is display, we’ll also make it the first responder.

3.5 Activity Indicator

As the API calls will be done asynchronously, we’ll need to show the progress to the user whilst preventing her from move to another screen. For this we’ll use the SwiftSpinner library from the prolific Marin Todorov. All you need to do is download the SwiftSpinner.swift file and add it to the Xcode project.

3.6 Starting the Verification

The login action will:

  • save number to UserDefaults
  • show the SwiftSpinner
  • and start a verification process

There is just one more thing we need to do before we move to the next screen — if a verification is in progress, we should automatically advance to the next screen. We can achieve this in viewDidLoad by checking if the requestId UserDefault is set. Here is the complete listing of LoginViewController:

3.7 Fulfilling the Verification

Once the number is submitted, we should receive an SMS with a 4-digit code that we’ll use to confirm our number.

To submit this, let’s build the interface for the CodeViewController — as before, we’ll need a TextField but no submit button as we’ll send the code once 4 digits are entered. We’ll also add an IBOutlet for it:

We’ll also need to disable the navbar back button and replace it with a cancel button that is displayed 30 seconds later. NB: Nexmo verification requests can’t be cancelled in the first 30 seconds.

The cancellation is straight-forward — a call to VerifyService cancel and going back for either success or error:

Let’s now handle the code submission — we’ll need to add an IBAction for the “Editing Changed” event of the codeTextField (the Connections Inspector is your friend).

That’s it for the CodeViewController — a complete listing can be found at: https://gist.github.com/pardel/c28eecec05239ce5efbc5c3058ae503e

3.7 Main Screen

Once verified, our app is showing a blank white screen. Let’s change that a bit to reflect the authenticated status.

Let’s open the Main.storyboard and embed the ViewController into a Navigation Controller just like we did in section 3.1.

Let’s also add, in ViewController.swift, a title and a logout button:

Build & Run and we’re off to the races!

And here we have it! A complete iOS app that doesn’t require user credentials to authenticate its users!

A complete copy of the project is available at: https://github.com/pardel/NexmoVerify-iOS

--

--

Paul Ardeleanu
Paul Ardeleanu’s Blog

iOS & Web engineer, trainer and coach - with a dash of UX & UI.