Skip to content

Instantly share code, notes, and snippets.

@JessicaG
Last active June 13, 2017 15:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JessicaG/ae710422163a42482faa1678075bf5b4 to your computer and use it in GitHub Desktop.
Save JessicaG/ae710422163a42482faa1678075bf5b4 to your computer and use it in GitHub Desktop.
Nexmo Verify API with Swift and Javasript

Getting started with Nexmo 2FA using a Node Server and Swift Application

NotelyServer NotelyApp

Before you begin, our tutorial assumes a few things:

  • You have a server/some endpoints you can serve to your iOS application.
  • You are using a dependency manager like Carthage or Cocoa Pods
  • You have secured your API keys from Nexmo (very quick and easy process!)

That being said, let’s get started!

Your Server

We are using a node server for this portion to make our http calls to the Node server, but you can use whichever language suits you!

You can use the branch gettting-started to look at where to kick off our server.

We are using Axios here for our calls, a Promise based HTTP client for the browser and node.js to keep things lightweight and simple in correlation with Express, a fast, unopinionated, minimalist web framework for node.

So, let’s get started with our calls, we need a call to hit the Verify API with a verify request and a verify check.

To keep things organized, let’s create a file just for this verify api call, we’ll call it verify_api.js

First, let’s bring in Axios as a constant, along with our api keys. Now we have everythign we need to create a function to make our call.

function verifyRequest(number) {
  const url = 'https://api.nexmo.com/verify/json'
  return axios.post(url,{
    api_key: nexmoApiKey,
    api_secret: nexmoApiSecretKey,
    number,
    brand: 'Notely'
  })
}

For our verifyRequest function, we’ll want to take in the phone number from the user, this is really all we need for this call. We set up our url and then continue to our call. Since this is based on a promise, we need to explicitly return what we get from our call. So, we are going to use axios.post to post to our Nexmo api call. Here we are using our url, with the api key, secret, number and our brand.

The brand is important to note here, Nexmo uses this parameter in the message to our user on our behalf. So, here is we have this hard coded to our application name, however if you want this to change per call or user, simply add this as an additional parameter to pass into our verifyRequest call and then utilize in the call like we are doing with the number variable.

Important to note, to utilize these functions, make sure you export your functions like so:

module.exports = {verifyRequest}

This allows us to use the verifyRequest on this class outside of this file (aka in our server).

Awesome! We’re close to making our first call, the next step is creating an endpoint for this function to be called from another application (our iOS application).

Let’s look at our server.js file, we have a few things set up for us to parse our body, and a router using Express. We want to set up our endpoints to be used locally, staging and production, and we’ll use some best practice api namespacing to kick things off.

router.get('/api/v1/authenticate/:number', function(req, res, next){
  verifyApi.verifyRequest(req.params.number)
    .catch(error => console.log(error) || res.status(500).send(error))
    .then(response => res.json(response.data))
})

Here we are using our router for a get request taking in a number as a url parameter and then our function has a request, response and next. We are now calling the function we created in the other class and taking the number from our parameters (req.params.number) and passing it through to our our function.

Currently, for demo purposes, we are logging the error and returning a 500 with the error; however in production environments, you’ll want to be a bit more robust and showcase errors to the user based on your applications requirements.

Upon success, we are parsing the json and returning the data from the response.

Awesome, you’ve created your first API call with Nexmo! 👏👏👏 Now that we’ve dove into those calls, we can whip through the next one.

The call we just created is step one of two for the verification process. Once we have received the user’s phone number and verified it is correct, we send them a code. We need to now consume the code on our end and match it with the user input to ensure it is correct. Let’s dive in.

We need to write a function in our verify_api.js file to consume the request_id and the code from the user and check against what we have. The request_id comes from Nexmo, this will be unique per request that comes in and this is how Nexmo keeps track of the requests coming in for the Verify API.

function verifyCheck({request_id, code}) {
  const url = 'https://api.nexmo.com/verify/check/json'
  return axios.post(url, {
    api_key: nexmoApiKey,
    api_secret: nexmoApiSecretKey,
    request_id,
    code
  })
}

Similar to our earlier call, we’ll need to instantiate a url, then use it in our call (note, this is a post rather than a get), alongside our api key,secret key and the request_id and code we have passed into this function.

Be sure to add this newly added function to our exports:

module.exports = {verifyRequest, verifyCheck}

Now back to our server.js file, so close!

We are going to create another endpoint to hit with our verify section. Since we have more than one parameter, you’ll note we are taking the body of the request, rather than taking anything from the URL parameters like we did in the last call.

router.post('/api/v1/authenticate/verify', function(req, res, next){
  verifyApi.verifyCheck(req.body)
    .catch(error => console.log(error) || res.status(500).send(error))
    .then(response => res.json(response.data))
})

Similar to our last call, we are logging and sending our error up if we get one, and upon success, we send the data of the response body.

¡Voila! We are done with our server, onto the fun part, our iOS application to consume the endpoints we just created.

Your Application

You can use the branch gettingStarted to look at the base aspects of this application for the iOS application.

For where we are in the gettingStarted branch, we have started a single page application, and brought in CocoaPods with AlamoFire for our HTTP calls.

We are utilizing AlamoFire to assist with memory leaks that we typically get out of our out of the box HTTP calls or AFNetworking.

For time purposes, let’s focus on the 2FA side of things after a login, so we’ll jump right into a view to enter a user phone number.

We need to create a text field for the user to enter their phone number. We can start in our storyboard (Main.storyboard) to create the field.

On your menu bar to the right, find the search area, type in “text” to find text field, then hold down CTRL while dragging the text field over to your storyboard. Once you have that in position for where you want it, be sure to find the keyboard type under the Attributes section and select “Number Pad” so the user can have a different type of keyboard. To make things easier on us, let’s add in a placeholder text to see and we’ll add a border to see things.

Next up, let’s put in a quick text field to let the user what we’re looking for from them and a button to allow them to tell us when they are ready for us to authenticate them!

Now that we have our first storyboard view set up, let’s head to our controller. By default, Swift gives us a DataViewController and a RootController when we create a single page application. Let’s dig into the the DataViewController.

We need to tie our fields from our storyboard to an IBOutlet in our view controller, we can do this by bringing up the screens side by side, select the view field, hold CTRL and drag over to the ViewController. Here we should see options for IBOutlets and IBActions. We want to select IBOutlets for now just so we have reference to these fields as variables in our View Controllers. Since our instruction text is not dynamic, we only need to tie our phoneNumberField and our sendTextButton.

    @IBOutlet weak var phoneNumberField: UITextField!
    @IBOutlet weak var sendTextButton: UIButton!

Once we see the circles to the left of the outlets filled, we know they are tied to the view. We can also check under the storyboard under the connections inspector. Note we have weak connections to these outlets, this is to help with memory leaks, since we don’t need these variables past this view, we instantiate with a weak vs a strong variable.

We also need an action associated with this page, specifically, with the button. So let’s create that. Select and CTRL drag over the controller to create an IBAction, which we can call sendUserText.

@IBAction func sendUserText(_ sender: Any) {
        self.getRequestToken()
    }

Let’s create a function called getRequestToken and call it within here so we can call our endpoint we created earlier.

We want to use this function to set up our success and failure from our api call, the only thing we need to pass at this point is the value from the text field with the phone number.

// Verify Request
   func getRequestToken() {
       self.performVerifyRequest(self.phoneNumberField.text!,
                                 success: {
                                   (response : DataResponse<Any>)
                                   in
                                   let storyboard = UIStoryboard(name: "verifyView", bundle: nil)
                                   let theViewController = storyboard.instantiateInitialViewController() as?UITabBarController
                                   let window = UIApplication.shared.delegate?.window
                                   window??.rootViewController = theViewController
                                   self.present(theViewController!, animated: true, completion: nil)
       },
                                 failure: {
                                   (response : DataResponse<Any>)
                                   in
                                   if let statusCode = response.response?.statusCode {
                                       switch(statusCode) {
                                       case 500 :
                                           let title = "Invalid Phone Number"
                                           let message = "The number you provided was invalid, please try again."
                                           self.alertController.alertUserWithNoAction(alertTitle: title, alertMessage: message, controller: self)
                                       default :
                                           break
                                       }
                                       
                                   }
                                   
                                   
       }
       )
   }

Okay, we have a lot going on here, let’s break out bit by bit.

Within this function, we are calling another function which we pass our phone number into. We are doing this to abstract phone calls and keep our Swift files clean and concise. This method is going to either get a code or not, and talk to the user via an alert.

We have abstracted our alertController into it’s own class so we can use it throughout our application with passing it a message and title.

Upon success, we instantiate a new controller view and change views once we have received a 200 go ahead from our server.

Let’s dig into our API call. We are receiving a phone number, a successProc and FailureProc. For AlamoFire, these need to be DataResponse objects and we can then dig into them more with our requests.

func performVerifyRequest(_ phone_number : String,
                             success successProc : ((DataResponse<Any>) -> ())?,
                             failure failureProc : ((DataResponse<Any>) -> ())? ) {

       let theToken : String = ""
       let urlString = "\(baseNotelyUrl)/authentication/verifyRequest"
       let headers : [String: String]? = ["Content-Type": "application/json",
                                          "token": theToken]
       let parameters : [String : Any] = [ "phone_number" : phone_number.trimmingCharacters(in: .whitespaces)]

       Alamofire.request(urlString,
                         method: .post,
                         parameters: parameters,
                         encoding: JSONEncoding.default,
                         headers: headers).validate().responseJSON {
                           response in
                           print(response.request as Any)  // original URL request
                           print(response.response as Any) // HTTP URL response
                           print(response.data as Any)     // server data
                           print(response.result)   // result of response serialization

                           if let JSON = response.result.value {
                               print("JSON: \(JSON)")
                           }
       }
}

We then need to set up our url string, headers and any parameters we want to send in. As a reminder, when we set up our endpoints in the beginning, we sent the phone number as a URL parameter, so we need to ensure we match that up here as well. We want to use JSONEncoding by default.

Once we have this built out, we have this very cool feature from AlamoFire that allows us to call validate().responseJSON and we get some validation on status codes off the bat and fall into a success failure block.

Let’s print everything to console to start off, so we can see what we are getting back.

To get these results, let’s spin up our server with an npm start and run on localhost, lets then build our XCode project and make sure we can get results back!


NEED TO ADD:

  • What we get back from our calls

  • Review storyboard for entering verification code Let's repeat the steps we followed before with the storyboard for the Verification screen

  • Review viewcontroller for consuming verification code

  • (Insert video of application running through it all?)



Conclusion

What have we learned?

  • We built a server to hit the Nexmo api and act as an endpoint for our iOS application. We used Express for our router and Axios for our http calls to Nexmo and we used environment variables to keep our API keys secure.
    • We built two end points to not only consume and send a user phone number to Nexmo, but also to use the requestId we get back and send to Nexmo to validate against the system with the code the user entered. (wow, who knew it was that easy?!?)
  • We used an iOS application to consumer user input and send to our server and then authenticate our user to move onto the rest of our application.
    • We worked with Storyboards, using IBOutlets, IBActions, Segues, ViewControllers, API calls, abstracting logic into different controllers and more!
    • We used AlamoFire for our HTTP calls, allowing us to easily validate against API calls with a success and failure.

All that being said, we were able to see our call from start to finish with a user input on their phone and being sent over the interwebs to our server and then onto Nexmo. Pretty cool, eh? Stay tuned for another post digging into User Authentication and using CoreData on your iOS application. You’re now ready to put some NexMO on your project and make it ‘mo awesome! Bye for now 👋

@leggetter
Copy link

leggetter commented Jun 13, 2017

@JessicaG - A couple of things I feel are missing that would help the flow:

  1. A clear statement of what is being built at the start. We go straight into building a server.
  2. Images of the iOS UI that's being built at various steps. Either within XCode or running in an emulator (if possible/appropriate) Just seen the images in the GDoc 👍

A few other comments:

  • I'm also assuming that we're not getting to the point where there are any notes?
  • There's a suggestion of login. But if we use the phone number as identity then the 2FA is login so we could remove that mention.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment