Skip to content

okta/okta-storage-swift

Repository files navigation

Version Carthage License Platform Swift

Okta Secure Storage Library

This library is a Swift wrapper around the iOS LocalAuthentication and Security frameworks. The library provides convenient APIs to utilize keychain services by giving your app a mechanism to store small bits of user data in an encrypted database. The keychain is not limited to passwords and tokens. You can store other secrets that the user explicitly cares about, such as credit card information or even short notes.

The storage library includes the following features:

  1. Get, set and delete keychain items.
  2. Store to the keychain behind a biometric factor such as fingerprint or Face ID.

Table of Contents

Usage

Add import OktaSecureStorage to your source code.

Create instance of OktaSecureStorage class

let oktaStorage = OktaSecureStorage()
or
let oktaStorage = OktaSecureStorage(applicationPassword: "user_password")

Save data to keychain

do {
    try oktaStorage.set(string: "password", forKey: "jdoe")
} catch let error {
    // Handle error
}

Save data to keychain behind a biometric factor

do {
    try oktaStorage.set(string: "password", forKey: "jdoe" behindBiometrics: true)
} catch let error {
    // Handle error
}

Load data from keychain

do {
    let password = try oktaStorage.get("jdoe")
} catch let error {
    // Handle error
}

Also, if you need to know what keys are already stored:

do {
    let keys = try oktaStorage.getStoredKeys()
} catch let error {
    // Handle error
}

Delete data from keychain

do {
    try oktaStorage.delete("jdoe")
} catch let error {
    // Handle error
}

API Reference

init(applicationPassword:)

Initializes OktaSecureStorage instance. The optional parameter applicationPassword allows items in the keychain to be secured using an additional password. This way, if the user does not have a passcode or Touch ID set up, the items will still be secure, and it adds an extra layer of security if they do have a passcode set

set(string:forKey:)

Stores an item securely in the keychain. Method returns true on success and false on error.

do {
    try oktaStorage.set("password", forKey: "jdoe")
} catch let error {
    // Handle error
}

set(string:forKey:behindBiometrics:)

Stores an item securely and additionally accepts behindBiometrics parameter. Set this parameter to true if you want to store keychain item behind a biometric factor such as touch ID or face ID.

do {
    try oktaStorage.set("password", forKey: "jdoe" behindBiometrics: true)
} catch let error {
    // Handle error
}

set(string:forKey:behindBiometrics:accessGroup:)

Stores an item securely and additionally accepts accessGroup identifier. Use accessGroup to share keychain items between apps. Two or more apps that are in the same group can share keychain items because they share a common keychain access group entitlement. For more details, see Sharing Access to Keychain Items Among a Collection of Apps

do {
    try oktaStorage.set("password",
                        forKey: "jdoe",
                        accessGroup: "TEAMSEEDID.com.mycompany.sharedkeychain")
} catch let error {
    // Handle error
}

set(string:forKey:behindBiometrics:accessibility:)

Stores an item securely and additionally accepts accessibility parameter. Use accessibility parameter to indicate when a keychain item is accessible. Choose the most restrictive option that meets your app’s needs so that the system can protect that item to the greatest extent possible. Possible values are listed here. Please note that default value for accessibility parameter is kSecAttrAccessibleWhenUnlockedThisDeviceOnly - items with this attribute do not migrate to a new device.

do {
    try oktaStorage.set("password",
                        forKey: "jdoe",
                        accessibility: kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
} catch let error {
    // Handle error
}

Additional helper functions

set(string: String,
    forKey key: String,
    accessGroup: String? = nil,
    accessibility: CFString?) throws

set(data: Data,
    forKey key: String) throws
    
set(data: Data,
    forKey key: String,
    behindBiometrics: Bool) throws

set(data: Data,
    forKey key: String,
    behindBiometrics: Bool,
    accessGroup: String) throws

set(data: Data,
    forKey key: String,
    behindBiometrics: Bool,
    accessibility: CFString) throws

set(data: Data,
    forKey key: String,
    accessGroup: String?,
    accessibility: CFString?) throws

get(key:biometricPrompt:)

Retrieves the stored keychain item from the keychain. Additionally method expects optional prompt message for the keychain item stored behind a biometric factor.

  • Note: iOS will show native Touch ID or Face ID message view in case of biometrics enabled storage. It means that function may be blocked and wait for the user's action. It is advised to call get function in a background thread
DispatchQueue.global().async {
    do {
        let password = try oktaStorage.get("jdoe", prompt: “Please use Touch ID or Face ID to sign in”)
    } catch let error {
        // Handle error
    }
}

getData(key:biometricPrompt:)

Retrieves the stored keychain item from the keychain. Additionally method expects optional prompt message for the keychain item stored behind a biometric factor.

  • Note: iOS will show native Touch ID or Face ID message view in case of biometrics enabled storage. It means that function may be blocked and wait for the user's action. It is advised to call getData function in a background thread
DispatchQueue.global().async {
    do {
        let passwordData = try oktaStorage.getData("jdoe", prompt: “Please use Touch ID or Face ID to sign in”)
    } catch let error {
        // Handle error
    }
}

getStoredKeys(biometricPrompt:accessGroup:)

Retrieves previously stored keys from the keychain. Additionally method expects optional prompt message for the keychain item stored behind a biometric factor. Use accessGroup to access shared keychain items between apps.

  • Note: Similarly to getData function, iOS will show native Touch ID or Face ID message view in case of biometrics enabled storage. It means that function may be blocked and wait for the user's action. It is advised to call getStoredKeys function in a background thread.
DispatchQueue.global().async {
    do {
        let keys = try oktaStorage.getStoredKeys()
    } catch let error {
        // Handle error
    }
}

delete(key:)

Removes the stored keychain item from the keychain

do {
    try oktaStorage.delete("jdoe")
} catch let error {
    // Handle error
}

clear()

Removes all keychain items from the keychain

do {
    try oktaStorage.clear()
} catch let error {
    // Handle error
}

isTouchIDSupported

Checks whether device enrolled with Touch ID. If the biometry is not available, not enrolled or locked out, then the function call will return false.

let isTouchIDSupported = storageManager.isTouchIDSupported()

isFaceIDSupported

Checks whether device enrolled with Face ID. If the biometry is not available, not enrolled or locked out, then the function call will return false.

let isFaceIDSupported = storageManager.isFaceIDSupported()

How to use this library in Objective-C project

  1. Include auto generated swift header file into your .m file. Swift header file contains objective-c representation of Okta swift classes. Please note that the name of header file consists of your project name and “-Swift” suffix. For example if your project name is AuthApp, then auto generated header file name will be “AuthApp-Swift.h”
  2. Start using programming components available in swift header file

Example:

OktaSecureStorage *storage = [OktaSecureStorage new];
NSError *error;
BOOL success = [storage setWithString:"password" forKey:"jdoe" error:&error];
if (success) {
    NSString *password = [storage getWithKey:@"jdoe" error:&error];
    if (password != nil) {
        success = [storage deleteWithKey:@"jdoe" error:&error];
    }
}

Contributing

We're happy to accept contributions and PRs! Please, read contributing guide.