tauri-plugin-secure-storage 1.4.0

Tauri plugin for secure storage using the system's keyring.
Documentation
import SwiftRs
import Tauri
#if canImport(UIKit)
import UIKit
#endif
import WebKit
import KeychainSwift

class PingArgs: Decodable {
    let value: String?
}

class SecureStoragePlugin: Plugin {
    
    let kKeyOption = "prefixedKey"
    let kDataOption = "data"
    let kSyncOption = "sync"
    let kAccessOption = "access"
    let keychain = KeychainSwift()
    
    @objc func setSynchronizeKeychain(_ call: Invoke) {
        keychain.synchronizable = getSyncParam(from: call)
        call.resolve()
    }
    
    @objc func setItem(_ call: Invoke) {
        // The getters will reject the call if the param is missing
        guard let key = getKeyParam(from: call),
              let data = getDataParam(from: call) else {
            return
        }
        
        let access = getAccessParam(from: call)
        
        tryKeychainOp(call, getSyncParam(from: call)) {
            try storeData(data, withKey: key, access: access)
            call.resolve()
        }
    }
    
    @objc func getItem(_ call: Invoke)  {
        guard let key = getKeyParam(from: call) else {
            return
        }
        
        tryKeychainOp(call, getSyncParam(from: call)) {
            let data = getData(withKey: key)
            call.resolve(["data": data])
        }
    }
    
    @objc func removeItem(_ call: Invoke) {
        guard let key = getKeyParam(from: call) else {
            return
        }
        
        tryKeychainOp(call, getSyncParam(from: call)) {
            let success = try deleteData(withKey: key)
            call.resolve(["success": success])
        }
    }

    func getKeyParam(from call: Invoke) -> String? {
        do {
            let args = try call.getArgs()
            
            if let key = args[kKeyOption] as? String,
               !key.isEmpty {
                return key
            }
            
            KeychainError.reject(call: call, kind: .missingKey)
            return nil
        } catch {
            return nil
        }
    }
    
    func getDataParam(from call: Invoke) -> String? {
        do {
            let args = try call.getArgs()
            
            if let data = args[kDataOption] as? String {
                return data
            }
            
            KeychainError.reject(call: call, kind: .invalidData)
            return nil
        } catch {
            return nil
        }
    }
    
    func getSyncParam(from call: Invoke) -> Bool {
        do {
            let args = try call.getArgs()
            if let value = args[kSyncOption] as? Bool {
                return value
            }
            
            return keychain.synchronizable
        } catch {
            return keychain.synchronizable
        }
    }
    
    func getAccessParam(from call: Invoke) -> KeychainSwiftAccessOptions? {
        do {
            let args = try call.getArgs()
            if let value = args[kAccessOption] as? Int {
                switch value {
                case 0:
                    return KeychainSwiftAccessOptions.accessibleWhenUnlocked
                    
                case 1:
                    return KeychainSwiftAccessOptions.accessibleWhenUnlockedThisDeviceOnly
                    
                case 2:
                    return KeychainSwiftAccessOptions.accessibleAfterFirstUnlock
                    
                case 3:
                    return KeychainSwiftAccessOptions.accessibleAfterFirstUnlockThisDeviceOnly
                    
                case 4:
                    return KeychainSwiftAccessOptions.accessibleWhenPasscodeSetThisDeviceOnly
                    
                default:
                    return nil
                }
            }
        } catch{
            
        }
        
        return nil
    }
    
    func tryKeychainOp(_ call: Invoke, _ sync: Bool, _ operation: () throws  -> Void) {
        var err: KeychainError?
        
        let saveSync = keychain.synchronizable
        keychain.synchronizable = sync
        
        do {
            try operation()
        } catch let error as KeychainError {
            err = error
        } catch {
            err = KeychainError(.unknownError)
        }
    }
    
    func storeData(_ data: String, withKey key: String, access: KeychainSwiftAccessOptions?) throws {
      let success = keychain.set(data, forKey: key, withAccess: access)

      if !success {
        throw KeychainError(.osError)
      }
    }

    func getData(withKey key: String) -> Any {
      keychain.get(key) as Any
    }

    func deleteData(withKey key: String) throws -> Bool {
      let success = keychain.delete(key)

      if !success && keychain.lastResultCode != 0 && keychain.lastResultCode != errSecItemNotFound {
        throw KeychainError(.osError)
      }

      return success
    }
}

@_cdecl("init_plugin_secure_storage")
func initPlugin() -> Plugin {
    return SecureStoragePlugin()
}