Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Example/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ final class ViewController: UIViewController {
private var oktaAppAuth: OktaOidc?
private var authStateManager: OktaOidcStateManager? {
didSet {
authStateManager?.writeToSecureStorage()
do {
try authStateManager?.writeToSecureStorage()
} catch {
print("Error writeToSecureStorage")
}
}
}

Expand Down Expand Up @@ -58,7 +62,11 @@ final class ViewController: UIViewController {
oktaAppAuth = try? OktaOidc(configuration: isUITest ? testConfig : configuration)
AppDelegate.shared.oktaOidc = oktaAppAuth
if let config = oktaAppAuth?.configuration {
authStateManager = OktaOidcStateManager.readFromSecureStorage(for: config)
do {
authStateManager = try OktaOidcStateManager.readFromSecureStorage(for: config)
} catch {
print("Error readFromSecureStorage")
}
authStateManager?.requestCustomizationDelegate = self
}
}
Expand Down
16 changes: 13 additions & 3 deletions Sources/OktaOidc/Common/OktaOidcKeychain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,19 @@ public class OktaOidcKeychain: NSObject {
SecItemDelete(cfDictionary)

let sanityCheck = SecItemAdd(cfDictionary, nil)
if sanityCheck != noErr {
throw OktaOidcKeychainError.failed(sanityCheck.description)
}
if sanityCheck != errSecSuccess {
var message: String = ""
if #available(iOSApplicationExtension 11.3, *) {
let message = SecCopyErrorMessageString(sanityCheck, nil) as String? ?? "Unknown error"
} else {
let message = "Keychain error (status: \(sanityCheck))"
}
let errorDesc = "Keychain set failed (status: \(sanityCheck)) - \(message)"
print("❌ [Keychain] \(errorDesc)")
throw OktaOidcKeychainError.failed(errorDesc)
} else {
print("✅ [Keychain] Successfully stored item for key: \(key)")
}
}

/**
Expand Down
58 changes: 27 additions & 31 deletions Sources/OktaOidc/Common/OktaOidcStateManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -191,50 +191,46 @@ open class OktaOidcStateManager: NSObject, NSSecureCoding {
@objc public extension OktaOidcStateManager {

@available(*, deprecated, message: "Please use readFromSecureStorage(for config: OktaOidcConfig) function")
class func readFromSecureStorage() -> OktaOidcStateManager? {
return readFromSecureStorage(forKey: "OktaAuthStateManager")
class func readFromSecureStorage() throws -> OktaOidcStateManager {
return try readFromSecureStorage(forKey: "OktaAuthStateManager")
}

@objc class func readFromSecureStorage(for config: OktaOidcConfig) -> OktaOidcStateManager? {
return readFromSecureStorage(forKey: config.clientId)
@objc class func readFromSecureStorage(for config: OktaOidcConfig) throws -> OktaOidcStateManager {
return try readFromSecureStorage(forKey: config.clientId)
}

@objc func writeToSecureStorage() {
@objc func writeToSecureStorage() throws {
let authStateData: Data
do {
if #available(iOS 11, OSX 10.14, *) {
authStateData = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
} else {
authStateData = NSKeyedArchiver.archivedData(withRootObject: self)
}

try OktaOidcKeychain.set(
key: self.clientId,
data: authStateData,
accessibility: self.accessibility
)
} catch let error {
print("Error: \(error)")
if #available(iOS 11, OSX 10.14, *) {
authStateData = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
} else {
authStateData = NSKeyedArchiver.archivedData(withRootObject: self)
}

try OktaOidcKeychain.set(
key: self.clientId,
data: authStateData,
accessibility: self.accessibility
)
}

private class func readFromSecureStorage(forKey secureStorageKey: String) -> OktaOidcStateManager? {
guard let encodedAuthState: Data = try? OktaOidcKeychain.get(key: secureStorageKey) else {
return nil
}

let state: OktaOidcStateManager?
private class func readFromSecureStorage(forKey secureStorageKey: String) throws -> OktaOidcStateManager {
let encodedAuthState: Data = try OktaOidcKeychain.get(key: secureStorageKey)
prepareKeyedArchiver()

if #available(iOS 11, OSX 10.14, *) {
state = (try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(encodedAuthState)) as? OktaOidcStateManager
guard let state = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(encodedAuthState) as? OktaOidcStateManager else {
throw OktaOidcKeychainError.failed("Failed to decode OktaOidcStateManager for key: \(secureStorageKey)")
}
return state
} else {
state = NSKeyedUnarchiver.unarchiveObject(with: encodedAuthState) as? OktaOidcStateManager
guard let state = NSKeyedUnarchiver.unarchiveObject(with: encodedAuthState) as? OktaOidcStateManager else {
throw OktaOidcKeychainError.failed("Failed to decode (legacy) OktaOidcStateManager for key: \(secureStorageKey)")
}
return state
}

return state
}

/// This method can be removed in the future with release 4.0.0 or higher.
/// Resolves OKTA-427089
private static func prepareKeyedArchiver() {
Expand Down
8 changes: 4 additions & 4 deletions Tests/OktaOidcTests/OktaOidcStateManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,11 @@ class OktaOidcStateManagerTests: XCTestCase {
func runTestReadWriteToSecureStorage(with config: OktaOidcConfig) {
let manager = TestUtils.setupMockAuthStateManager(issuer: config.issuer, clientId: config.clientId, expiresIn: 5)

XCTAssertNil(OktaOidcStateManager.readFromSecureStorage(for: config))
XCTAssertNil(try? OktaOidcStateManager.readFromSecureStorage(for: config))

manager.writeToSecureStorage()
try? manager.writeToSecureStorage()

let storedManager = OktaOidcStateManager.readFromSecureStorage(for: config)
let storedManager = try? OktaOidcStateManager.readFromSecureStorage(for: config)
XCTAssertNotNil(storedManager)
XCTAssertEqual(
storedManager?.authState.lastAuthorizationResponse.accessToken,
Expand All @@ -382,7 +382,7 @@ class OktaOidcStateManagerTests: XCTestCase {
)

XCTAssertNoThrow(try manager.removeFromSecureStorage())
XCTAssertNil(OktaOidcStateManager.readFromSecureStorage(for: config))
XCTAssertNil(try? OktaOidcStateManager.readFromSecureStorage(for: config))
}
#endif
}
12 changes: 9 additions & 3 deletions Tests/OktaOidcTests/OktaOidcTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,10 @@ extension OktaOidcTests {
}
let viewController = UIViewController(nibName: nil, bundle: nil)
let options: OktaSignOutOptions = [.revokeAccessToken, .revokeRefreshToken, .removeTokensFromStorage]
authStateManager.writeToSecureStorage()
guard OktaOidcStateManager.readFromSecureStorage(for: createDummyConfig()!) != nil else {
try? authStateManager.writeToSecureStorage()
do {
try OktaOidcStateManager.readFromSecureStorage(for: createDummyConfig()!)
} catch {
XCTFail("Failed to read from secure storage")
return
}
Expand All @@ -236,8 +238,12 @@ extension OktaOidcTests {
XCTAssertEqual(numberOfRevokes, 2)
XCTAssertTrue(result)
XCTAssertTrue(failedOptions.isEmpty)
if OktaOidcStateManager.readFromSecureStorage(for: self.createDummyConfig()!) != nil {
do {
try OktaOidcStateManager.readFromSecureStorage(for: self.createDummyConfig()!)
XCTFail("Data has not been deleted from the secure storage")
} catch {
XCTFail("Failed to read from secure storage")
return
}
expectation.fulfill()
})
Expand Down