Creating Local Authentication SDK on CocoaPods

Creating Local Authentication SDK on CocoaPods

Step-by-step guide on how we created a biometric authentication SDK published on CocoaPods.
Su Ho
Su Ho
March 24, 2021
iOS Mobile

For most iOS app projects, we usually need to implement FaceID or TouchID as an authentication feature for the application. This means that developers often have to redo the same implementation from one project to another. To minimize unnecessary rework, I decided to create a reusable SDK to support current and upcoming projects.

This post will demonstrate how we have built and published a biometric authentication SDK called NimbleLocalAuthentication on CocoaPods.

Architecture

Local Authentication Framework

TouchID/FaceID is a biometric authentication technology based on a framework called Local Authentication which authenticates users biometrically or with a passphrase.

Within the Local Authentication framework, we focused on the class LAContext which implements a mechanism for evaluating authentication policies and access controls.

There are four available policies:

enum LAPolicy: Int {
	case deviceOwnerAuthenticationWithBiometrics
	case deviceOwnerAuthenticationWithWatch
	case deviceOwnerAuthenticationWithBiometricsOrWatch
	case deviceOwnerAuthentication
}

To assess whether authentication can proceed for a given policy, we used the method from the class LAContext:

func canEvaluatePolicy(LAPolicy, error: NSErrorPointer) -> Bool

And to evaluate the specified policy, we used the following method:

func evaluatePolicy(LAPolicy, localizedReason: String, reply: (Bool, Error?) -> Void) 

Keychain

When adding a biometric authentication feature into an application, there is always one additional feature that needs to be implemented: the ability to toggle the biometric authentication feature. However, this feature is not supported by default by the Local Authentication SDK.

I decided to add a dependency called KeychainAccess into our SDK to support the biometric authentication toggle feature.

KeychainAccess is used to store the value biometricEnabled internally and to access keychain objects solely in the NimbleLocalAuthentication SDK.

Project Setup

Prerequisites

  • Xcode latest version.
  • CocoaPods gem - Install by running [sudo] gem install cocoapods.

Create a pod project

We used the tool provided by the CocoaPods gem to create a project.

pod lib create NimbleLocalAuthentication
Successful pod creation output via the terminal
Successful pod creation output via the terminal

After creating the project, we configured the SDK in the file NimbleLocalAuthentication.podspec.

Pod::Spec.new do |s|

  s.name = 'NimbleLocalAuthentication'
  s.version = '1.0.0'
  s.summary = 'A SDK support local authentication.'
  s.homepage = 'https://github.com/nimblehq/local-authentication-ios'
  s.license = { :type => 'MIT', :file => 'LICENSE' }
  s.author = '@nimblehq'
  s.source = { :git => 'https://github.com/nimblehq/local-authentication-ios.git', :tag => s.version.to_s }
  s.ios.deployment_target = '11.0'
  s.source_files = 'Sources/Classes/**/*'
  s.swift_version = '5.0'
  s.framework = 'LocalAuthentication'
  s.dependency 'KeychainAccess'
end

When setting s.framework and s.dependency, the podspec file must use Local Authentication and KeychainAccess.

Implement the SDK

Implement the biometric authentication feature toggle

To implement the feature toggle, we created a class Keychain to wrap the library KeychainAccess and enum Keychain.Key to support getting and storing the value biometricEnabled.

final class Keychain { ... }

extension Keychain {	
	enum Key: String {
		case biometricEnabled
	}
}

We defined a protocol KeychainProtocol to access the data which is stored in Keychain internally.

protocol KeychainProtocol: AnyObject {

  subscript(bool key: Keychain.Key) -> Bool? { get set }
}

We implemented the protocol KeychainProtocol with the class Keychain.

extension Keychain: KeychainProtocol {

	subscript(bool key: Key) -> Bool? {
		// using KeychainAccesss library to get and set the data
		get { ... }
		set { ... }
  }
}

With this implementation, we can easily access or set the value biometricEnabled.

let keychain = Keychain()

// set the value
keychain[bool: .biometricEnabled] = true // or false

// access the value
keychain[bool: .biometricEnabled]

Implement the main class NimbleLocalAuthenticator

We defined a protocol named BiometryService for SDK consumers to access the features of Nimble Local Authentication SDK.

import LocalAuthentication

public protocol BiometryService: AnyObject {

  /// A boolean value stating whether the biometric feature is enabled.
  var isEnabled: Bool { get set }

  /// A boolean value stating whether the biometric feature is available.
  var isAvailable: Bool { get }

  /// Indicates the type of biometry supported by the device.
  var supportedType: BiometryType { get }

  /// Fallback button title.
  var localizedFallbackTitle: String? { get set }

  /// Cancel button title.
  var localizedCancelTitle: String? { get set }

  /// Allows setting the default localized authentication reason to show to the user.
  var localizedReason: String { get set }

  /// Allows running authentication in non-interactive mode.
  var interactionNotAllowed: Bool { get set }

  /// Authenticates the user when using the application.
  ///
  /// @param completion Completeion block that is executed when authentication finishes.
  ///              success Completion parameter that is success if the authentication has been excuted successfully.
  ///              failure Completion parameter that is failed if the authentication has been authenticate failed.
  ///                    contains error information about the authentication's failure.
  ///
  /// @see BiometryError
  func authenticate(completion: @escaping (BiometryResult) -> Void)

  /// Invalidates the biometry service
  func invalidate()

  /// Credential provided by application
  ///
  /// Sets a credential to this service
  ///
  /// @param credential Credential to be used with subsequent calls. Setting this parameter to nil will remove
  ///                   any existing credential of the specified type.
  ///
  /// @param type CredentialType of the provided credential.
  ///
  /// @return YES if the credential was set successfully, NO otherwise.
  func setCredential(_ credential: Data?, type: CredentialType) -> Bool

  /// Reveals if credential was set with this context.
  ///
  /// @param type CredentialType of credential we are asking for.
  ///
  /// @return YES on success, NO otherwise.
  func isCredentialSet(_ type: CredentialType) -> Bool
}

In the SDK, we implemented the protocol BiometryService with the class NimbleLocalAuthenticator and made it public.

public final class NimbleLocalAuthenticator { ... }

extension NimbleLocalAuthenticator: BiometryService { ... }

Publish The Pod

Release and tag the correct version

We released the version 1.0.0 (as defined in podspec file), then tagged the version on Git:

git tag 1.0.0
git push origin 1.0.0

Verify the SDK

Before publishing the Nimble Local Authentication SDK to CocoaPods, we checked if the podspec lints correctly:

pod lib lint NimbleLocalAuthentication.podspec
Successful linting output of the podspec file
Successful linting output of the podspec file

Publish the SDK

After the SDK passed validation, publishing the SDK on CocoaPods was done with the following command.

pod trunk push NimbleLocalAuthentication.podspec

Technically, the command published the spec to the CocoaPods’ Specs repository.

Upon success, the output was:

--------------------------------------------------------------------------------
 🎉  Congrats

 🚀  NimbleLocalAuthentication (1.0.0) successfully published
 📅  February 5th, 14:33
 🌎  https://cocoapods.org/pods/NimbleLocalAuthentication
 👍  Tell your friends!
--------------------------------------------------------------------------------

Wrapping up

Using Local Authentication and KeychainAccess, the Nimble iOS team created an SDK called NimbleLocalAuthentication which acts as a wrapper for Apple’s frameworks and provides additional features that will support iOS developers to easily implement TouchID/FaceID in any application 🎉.

If you have any questions, issues, or features, go to our repository. Issues and pull requests are welcome!

If this is the kind of challenges you wanna tackle, Nimble is hiring awesome web and mobile developers to join our team in Bangkok, Thailand, Ho Chi Minh City, Vietnam, and Da Nang, Vietnam✌️

Join Us

Recommended Stories:

Accelerate your digital transformation.

Subscribe to our newsletter and get latest news and trends from Nimble