iOS Quick Start Guide

By implementing the ZenKey SDK in your iOS app, users can interact with the ZenKey button, initiating an authorization flow between your secure server and the ZenKey backend. To integrate the ZenKey SDK in your iOS application, follow the steps below.

1. Enroll in the ZenKey Service Provider Portal

Visit the ZenKey Service Provider Portal and create an account for your organization.

ZenKey Service Provider Portal

Once logged in:

  1. Create a Project.
  2. Designate a Security Contact.
  3. Retrieve your Client ID and Secret.

Note: Because ZenKey must verify and approve your company’s application, you may need to wait 24-48 hours before your Client ID is officially registered and deemed valid.

2. Include the ZenKey SDK in your Project

Now include the ZenKey SDK in your project as a CocoaPod by adding the following code to your podfile:

    pod 'ZenKeySDK'

Or add the SDK source manually to your Xcode project:

  1. Move the ZenKey SDK source directly to the project directory.
  2. Add ZenKeySDK.xcodeproj to your application's Xcode project.
  3. After adding the project, confirm that your deployment target is greater than or equal to the SDK deployment target (in Xcode: Project > Info Panel).
  4. View your project's "Embedded Binaries" under your target's "General" panel. Add the ZenKeySDK framework. Be sure to select the corresponding framework for the platform you are targeting (the iOS framework for an iOS target).
  5. Build and run the project to ensure that everything is working correctly.

3. Configure Your Client ID

Open your project’s Info.plist and follow the steps below. Once complete, your Info.plist should resemble the sample shown here:

Plist Example
  1. First, copy your client_id from the ZenKey Service Provider Portal dashboard.
  2. Next, open your application’s Info.plist. Add a ZenKeyClientId key, then paste your client_id as the string value:
    <key>ZenKeyClientId</key>
    <string>{client_id}</string>
  1. Now add CFBundleURLTypes — the list of URL schemes supported by the app — as a key:
    <key>CFBundleURLTypes</key>     

After adding this, Xcode will create an array with a "Type 0" dictionary containing a CFBundleURLName key.

  1. Add CFBundleTypeRole and CFBundleURLSchemes to the "Type 0" dictionary. If your project already has a CFBundleURLTypes key with an existing Type dictionary, this new data may still be added at any index.

  2. Add your client_id a second time, also as a string, under CFBundleURLSchemes:

    <key>CFBundleURLSchemes</key>     
    <array>
    <string>{client_id}</string>
    </array>

4. Instantiate ZenKey

To support the ZenKey SDK within your application, you must instantiate ZenKey in the application delegate as follows:

import ZenKeySDK
...
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication,
     didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        ZenKeyAppDelegate.shared.application(
            application,
            didFinishLaunchingWithOptions: launchOptions
        )
        // Perform additional application setup.
        return true
    }
    func application(_ app: UIApplication,
                     open url: URL,
                     options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
        guard !ZenKeyAppDelegate.shared.application(app, open: url, options: options) else {
            return true
        }
        // Perform any other URL processing your app may need.
        return true
    }
}

5. Add the ZenKey Button

Create an instance of ZenKeyAuthorizeButton and add it to your UI. Set a delegate to handle authorization.

import ZenKeySDK  
class LoginViewController {  
  let zenKeyButton = ZenKeyAuthorizeButton()  
    override func viewDidLoad() {  
         super.viewDidLoad()  
          zenKeyButton.delegate = self  
     view.addSubview(zenKeyButton)  
    }  
}

6. Receive Authorization Code

In order to receive the authorization parameters and pass them to your secure backend, your button delegate must conform to ZenKeyAuthorizeButtonDelegate and handle buttonDidFinish(). If the user approves your request, buttonDidFinish() will be called with a .code result. The authorizedResponse will include the authorization code, mcc, mnc, redirectURI, and codeVerifier for you to send to your secure backend. You must also handle any .error or .cancelled result and inform the user that authorization was unsuccessful.

extension LoginViewController: ZenKeyAuthorizeButtonDelegate {  
      func buttonWillBeginAuthorizing(_ button: ZenKeyAuthorizeButton) {
       // Perform any UI updates like showing an activity indicator.  
     }  
    func buttonDidFinish(  
            _ button: ZenKeyAuthorizeButton,  
            withResult result: AuthorizationResult) {  
           // handle the outcome of the request:  
           switch result {  
           case .code(let authorizedResponse):  
                let code = authorizedResponse.code  
                let mccmnc = authorizedResponse.mccmnc
                let redirectURI = authorizedResponse.redirectURI
                let codeVerifier = authorizedResponse.codeVerifier  
                let nonce = authorizedResponse.nonce
                let context = authorizedResponse.context // string
                // Pass these identifiers to your secure server to perform a token request  
            case .error(let authorizationError):
                // There was an error with the authorization request  
            case .cancelled:  
                // The user cancelled their request in the ZenKey application  
          }  
     }
 }

7. API Call to Secure Server

While the ZenKey SDK handles the authorization request itself, ultimately you will need to configure a secure server that can receive the parameters from the authorization code request. Only once you receive these values may you request the carrier endpoint and issue a token request from your secure backend. Therefore, you must send an API call to your server which includes the parameters included in the authorization request. For example:

POST  
https://api.your-authorization-server.com/token
    mccmnc={mccmnc}
    &code={auth_code}   
    &redirect_uri={auth_redirect_uri}
    &code_verifier={codeVerifier}

8. Request Carrier Endpoints

Upon receiving the values above, your secure server now needs to get the right carrier endpoints for this user. This is done by requesting the OpenID Connect configuration with the help of two parameters: mccmnc and client_id. The mccmnc is set of 6 digits located on every SIM card identifying the country code (mcc) and operator code (mnc). Meanwhile, the client_id allows the carrier to identify your registered app.

GET
http://getdiscoveryissuer.myzenkey.com/well-known/openid_configuration?
    mccmnc=123456
    &client_id=ccid-xxxxxxxxxxxxx

The response will include all relevant endpoints.

{
    “issuer”: “https://mno.com”,
    “authorization_endpoint”: “https://mno.com/connect/authorize”,
    “token_endpoint”: https://mno.com/connect/token,
    “userinfo_endpoint”: “https://mno.com/connect/userinfo”,
…
}

9. Issue Token Request

With the user’s consent in the form of an authorization code, your secure server will request an access token from the token_endpoint discovered earlier. To configure this request:

  1. Base64 encode your client_id and client_secret:
        Base64Encode(“{client_id}:{client_secret}”)

Note: When you retrieve your client_secret from the ZenKey Service Provider Portal, for security purposes we ask that you only store your client_secret on your secure backend and allow as few trusted personnel as possible access to it.

  1. Insert this encoded value in your Authorization header:

         'Authorization': "Basic {encoded_value_here}"
  2. Specify the Content-Type as URL-encoded:

         Content-Type: application/x-www-form-urlencoded
  3. Then include the following parameters in the body of your token request:

    grant_type="authorization_code"
    code="{auth_code}"
    redirect_uri="{auth_redirect_uri}"
    code_verifier="{codeVerifier}"

To see all these components working together, see the example below:

POST /token HTTP/1.1
Host: mno.com
Authorization: Basic {encoded_value_here}
Content-Type: application/x-www-form-urlencoded

    grant_type=authorization_code
    &redirect_uri=https://www.client.com
    &code={auth_code}
    &code_verifier={codeVerifier}

10. Token Response

In addition to an Access Token, the token response will return an ID Token which includes the sub — an identifier that uniquely pairs a single user with a particular client_id. When you receive the sub, you will store this unique ID in your user database for reference. Do not transmit this sub to your client app.

Sample Token Response:

HTTP/1.1 200 OK
Content-type: application/json
Cache-control: no-cache

{
    "access_token": "....",
    "token_type": "Bearer",
    "refresh_token": "8xLOxBtZp8",
    "expires_in": 3600,
    "correlation_id”:”xxxxx”,
    "id_token": "..."
}
Decode ID Token

The ID Token will need to be decoded. Once you decode the JWT payload, you will see it contains claims about the authentication of your end user. As a best practice, you should always validate the ID Token after you have decoded it. Specifically, you should verify the signature and claims contained within the ID Token. For more information about each parameter, refer to our Server and Web Integration Guide.

Sample Decoded ID Token:

{   
    "type"="at+jwt",  
    "kid":"xxx"
}

{
    "iss": https://mno.com,
    "sub": "mccmnc-client0001",  
    "aud": "ccid-sp00001",
    "nonce": "n-0S6_WzA2Mj",
    "exp": 1311281970,
    "iat": 1311280970,
    "auth_time": 1311280969,
    "acr": "a3",
    ...
}

11. Request User Info

Upon receiving an Access Token, you may then request user info by issuing a GET request from your backend to the userinfo_endpoint discovered in Step 8.

GET /userinfo HTTP/1.1
Host: mno.com
Authorization: bearer {ACCESS_TOKEN}

The response will contain JSON with the user information requested by your scopes and approved by the user.

{
    "sub": "mccmnc-123456789",  
    "name": "Jane Doe",  
    "given_name": "Jane",  
    "family_name": "Doe",  
    "email": "janedoe@example.com",  
    "email_verified":"true",  
    "postal_code": "90210-3456",  
    "phone_number": "+13101234567",  
    "phone_number_verified":"true"  
}

Next Steps

If you don't want to set up ZenKey just yet, feel free to explore a few sample calls on our Developer Playground. This environment will help you to learn about each step in the authorization process.

ZenKey Developer Playground

For more specific information about the ZenKey app and SDK, visit the resources listed below:

Revision History

Date Published Document Version ZenKey SDK Version Description
3.02.20 0.0.02 1.0.0 Added nonce and context to Section 6 code sample.
1.31.20 0.0.01 1.0.0 First published version.

Last Update: Document Version 0.0.02, March 2, 2020