Line data Source code
1 : // 2 : // SwiftySimpleKeychain 3 : // SwiftySimpleKeychain 4 : // 5 : // Copyright (c) 2022 Ezequiel (Kimi) Aceto (https://eaceto.dev). Based on Auth0's SimpleKeychain 6 : // 7 : // Permission is hereby granted, free of charge, to any person obtaining a copy 8 : // of this software and associated documentation files (the "Software"), to deal 9 : // in the Software without restriction, including without limitation the rights 10 : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 : // copies of the Software, and to permit persons to whom the Software is 12 : // furnished to do so, subject to the following conditions: 13 : // 14 : // The above copyright notice and this permission notice shall be included in 15 : // all copies or substantial portions of the Software. 16 : // 17 : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 : // THE SOFTWARE. 24 : 25 : #if canImport(Foundation) 26 : import Foundation 27 : #endif 28 : 29 : #if canImport(LocalAuthentication) 30 : import LocalAuthentication 31 : #endif 32 : 33 : #if swift(<5.3) 34 : #error("Alamofire doesn't support Swift versions below 5.3") 35 : #endif 36 : 37 : /** 38 : * A simple helper class to deal with storing and retrieving values from Keychain. 39 : * It has support for sharing keychain items using Access Group and ffine grained accesibility 40 : * over a specific Kyechain Item (Using Access Control). 41 : * The support is only available for iOS and macOS, otherwise it will default using the coarse grained accesibility field. 42 : * When a `NSString` or `NSData` is stored using Access Control and the accesibility flag 43 : * `SwiftySimpleKeychainItem.accessibleAfterFirstUnlock`, iOS / macOS will prompt the user for 44 : * it's passcode or pass a TouchID challenge (if available). 45 : */ 46 : public class SwiftySimpleKeychain { 47 : 48 : /** 49 : * Service name under all items are saved. Default value is Bundle Identifier or "default" if not available 50 : */ 51 : private(set) var service: String 52 : 53 : /** 54 : * Access Group for Keychain item sharing. If it's nil no keychain sharing is possible. Default value is nil. 55 : */ 56 : private(set) var accessGroup: String? 57 : 58 : /** 59 : * What type of accessibility the items stored will have. 60 : * All values are translated to `kSecAttrAccessible` constants. 61 : * Default value is A0SimpleKeychainItemAccessibleAfterFirstUnlock. 62 : * @see kSecAttrAccessible 63 : */ 64 : private(set) var defaultAccessiblity: SwiftySimpleKeychainItemAccessible 65 : 66 : /** 67 : * Tells SwiftySimpleKeychain to use `kSecAttrAccessControl` instead of `kSecAttrAccessible`. 68 : * It will work only in iOS 8+, defaulting to `kSecAttrAccessible` on lower version. 69 : * Default value is False. 70 : */ 71 : private(set) var useAccessControl: Bool 72 : 73 : /** 74 : * LocalAuthenticationContext used to access items. Default value is a new LAContext object 75 : */ 76 : #if canImport(LocalAuthentication) 77 : private(set) var localAuthenticationContext: LAContext 78 : #endif 79 : 80 : /** 81 : * Instantiates a SwiftySimpleKeychain 82 : * 83 : * Usage: 84 : * ```swift 85 : * keychain = SwiftySimpleKeychain(with: "service-name") 86 : * ``` 87 : * 88 : * - Parameter service: **Service name** under all items are saved. Defaults to `default` 89 : * - Parameter accessGroup: an **Access Group** for Keychain item sharing. Defaults to `nil` 90 : */ 91 42 : public init(with service: String = "default", accessGroup: String? = nil) { 92 42 : self.service = service 93 42 : self.accessGroup = accessGroup 94 42 : self.defaultAccessiblity = .afterFirstUnlock 95 42 : self.useAccessControl = false 96 42 : 97 42 : // This does not apply to watchOS, tvOS and Linux 98 42 : // and all future platforms where LocalAuthentication is not available 99 42 : #if canImport(LocalAuthentication) 100 42 : localAuthenticationContext = LAContext() 101 42 : localAuthenticationContext.touchIDAuthenticationAllowableReuseDuration = 0 102 42 : #endif 103 42 : } 104 : 105 : /** 106 : * Sets the TouchID Authentication allowable reuse time 107 : * 108 : * Usage: 109 : * ```swift 110 : * keychain.setTouchIDAuthenticationAllowableReuse(0) // do not reuse authentication 111 : * ``` 112 : * 113 : * - Parameter duration: a TimeInterval that represents the allowable reuse time 114 : */ 115 3 : func setTouchIDAuthenticationAllowableReuse(duration: TimeInterval) { 116 3 : // This does not apply to watchOS, tvOS and Linux 117 3 : // and all future platforms where LocalAuthentication is not available 118 3 : #if canImport(LocalAuthentication) 119 3 : if duration <= 0 { 120 2 : localAuthenticationContext.touchIDAuthenticationAllowableReuseDuration = 0 121 3 : } else if duration >= LATouchIDAuthenticationMaximumAllowableReuseDuration { 122 1 : localAuthenticationContext.touchIDAuthenticationAllowableReuseDuration 123 1 : = LATouchIDAuthenticationMaximumAllowableReuseDuration 124 3 : } else { 125 2 : localAuthenticationContext.touchIDAuthenticationAllowableReuseDuration = duration 126 3 : } 127 3 : #endif 128 3 : } 129 : } 130 : 131 : public extension SwiftySimpleKeychain { 132 : 133 : /** 134 : * Instantiates a SwiftySimpleKeychain 135 : * 136 : * Usage: 137 : * ```swift 138 : * keychain = SwiftySimpleKeychain.with("service-name") 139 : * ``` 140 : * 141 : * - Parameter service: **Service name** under all items are saved. Defaults to `default` 142 : * - Parameter accessGroup: an **Access Group** for Keychain item sharing. Defaults to `nil` 143 : */ 144 3 : static func with(_ service: String = "default", accessGroup: String? = nil) -> SwiftySimpleKeychain { 145 3 : return SwiftySimpleKeychain(with: service, accessGroup: accessGroup) 146 3 : } 147 : }