From b86154a9aa808f40f87de39e32cf48e40534662e Mon Sep 17 00:00:00 2001 From: Guilherme Souza Date: Wed, 14 Aug 2024 15:03:44 -0300 Subject: [PATCH] fix(auth): store code verifier in keychain (#502) --- Sources/Auth/AuthClient.swift | 1 + Sources/Auth/Defaults.swift | 2 -- .../Auth/Internal/CodeVerifierStorage.swift | 32 ++++++++++++++++--- Sources/Auth/Internal/Contants.swift | 1 + Sources/Auth/Internal/Dependencies.swift | 3 +- Sources/Auth/Internal/SessionStorage.swift | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +-- Tests/AuthTests/MockHelpers.swift | 13 ++++++++ Tests/AuthTests/SessionManagerTests.swift | 1 + Tests/AuthTests/StoredSessionTests.swift | 1 + 10 files changed, 50 insertions(+), 10 deletions(-) diff --git a/Sources/Auth/AuthClient.swift b/Sources/Auth/AuthClient.swift index fcd8cb2d..d7289345 100644 --- a/Sources/Auth/AuthClient.swift +++ b/Sources/Auth/AuthClient.swift @@ -68,6 +68,7 @@ public final class AuthClient: Sendable { configuration: configuration, http: HTTPClient(configuration: configuration), api: APIClient(clientID: clientID), + codeVerifierStorage: .live(clientID: clientID), sessionStorage: .live(clientID: clientID), sessionManager: .live(clientID: clientID) ) diff --git a/Sources/Auth/Defaults.swift b/Sources/Auth/Defaults.swift index ae7e00fd..f293fe6b 100644 --- a/Sources/Auth/Defaults.swift +++ b/Sources/Auth/Defaults.swift @@ -57,6 +57,4 @@ extension AuthClient.Configuration { /// The default value when initializing a ``AuthClient`` instance. public static let defaultAutoRefreshToken: Bool = true - - static let defaultStorageKey = "supabase.auth.token" } diff --git a/Sources/Auth/Internal/CodeVerifierStorage.swift b/Sources/Auth/Internal/CodeVerifierStorage.swift index 2049b177..1df1e2c2 100644 --- a/Sources/Auth/Internal/CodeVerifierStorage.swift +++ b/Sources/Auth/Internal/CodeVerifierStorage.swift @@ -8,12 +8,36 @@ struct CodeVerifierStorage: Sendable { } extension CodeVerifierStorage { - static var live: Self { - let code = LockIsolated(String?.none) + static func live(clientID: AuthClientID) -> Self { + var configuration: AuthClient.Configuration { Dependencies[clientID].configuration } + var key: String { "\(configuration.storageKey ?? STORAGE_KEY)-code-verifier" } return Self( - get: { code.value }, - set: { code.setValue($0) } + get: { + do { + guard let data = try configuration.localStorage.retrieve(key: key) else { + configuration.logger?.debug("Code verifier not found.") + return nil + } + return String(decoding: data, as: UTF8.self) + } catch { + configuration.logger?.error("Failure loading code verifier: \(error.localizedDescription)") + return nil + } + }, + set: { code in + do { + if let code, let data = code.data(using: .utf8) { + try configuration.localStorage.store(key: key, value: data) + } else if code == nil { + try configuration.localStorage.remove(key: key) + } else { + configuration.logger?.error("Code verifier is not a valid UTF8 string.") + } + } catch { + configuration.logger?.error("Failure storing code verifier: \(error.localizedDescription)") + } + } ) } } diff --git a/Sources/Auth/Internal/Contants.swift b/Sources/Auth/Internal/Contants.swift index 308b544a..ae456410 100644 --- a/Sources/Auth/Internal/Contants.swift +++ b/Sources/Auth/Internal/Contants.swift @@ -8,3 +8,4 @@ import Foundation let EXPIRY_MARGIN: TimeInterval = 30 +let STORAGE_KEY = "supabase.auth.token" diff --git a/Sources/Auth/Internal/Dependencies.swift b/Sources/Auth/Internal/Dependencies.swift index fee5b772..ba1ce91d 100644 --- a/Sources/Auth/Internal/Dependencies.swift +++ b/Sources/Auth/Internal/Dependencies.swift @@ -6,12 +6,13 @@ struct Dependencies: Sendable { var configuration: AuthClient.Configuration var http: any HTTPClientType var api: APIClient + var codeVerifierStorage: CodeVerifierStorage var sessionStorage: SessionStorage var sessionManager: SessionManager var eventEmitter = AuthStateChangeEventEmitter() var date: @Sendable () -> Date = { Date() } - var codeVerifierStorage = CodeVerifierStorage.live + var urlOpener: URLOpener = .live var encoder: JSONEncoder { configuration.encoder } diff --git a/Sources/Auth/Internal/SessionStorage.swift b/Sources/Auth/Internal/SessionStorage.swift index 26664824..cd89e006 100644 --- a/Sources/Auth/Internal/SessionStorage.swift +++ b/Sources/Auth/Internal/SessionStorage.swift @@ -28,7 +28,7 @@ struct SessionStorage { extension SessionStorage { static func live(clientID: AuthClientID) -> SessionStorage { var key: String { - Dependencies[clientID].configuration.storageKey ?? AuthClient.Configuration.defaultStorageKey + Dependencies[clientID].configuration.storageKey ?? STORAGE_KEY } var oldKey: String { "supabase.session" } diff --git a/Supabase.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Supabase.xcworkspace/xcshareddata/swiftpm/Package.resolved index a0a59d70..8faf4b09 100644 --- a/Supabase.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Supabase.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -104,8 +104,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing", "state" : { - "revision" : "c097f955b4e724690f0fc8ffb7a6d4b881c9c4e3", - "version" : "1.17.2" + "revision" : "6d932a79e7173b275b96c600c86c603cf84f153c", + "version" : "1.17.4" } }, { diff --git a/Tests/AuthTests/MockHelpers.swift b/Tests/AuthTests/MockHelpers.swift index df43d504..e5c3210c 100644 --- a/Tests/AuthTests/MockHelpers.swift +++ b/Tests/AuthTests/MockHelpers.swift @@ -1,3 +1,4 @@ +import ConcurrencyExtras import Foundation import TestHelpers @@ -23,7 +24,19 @@ extension Dependencies { ), http: HTTPClientMock(), api: APIClient(clientID: AuthClientID()), + codeVerifierStorage: CodeVerifierStorage.mock, sessionStorage: SessionStorage.live(clientID: AuthClientID()), sessionManager: SessionManager.live(clientID: AuthClientID()) ) } + +extension CodeVerifierStorage { + static var mock: CodeVerifierStorage { + let code = LockIsolated(nil) + + return Self( + get: { code.value }, + set: { code.setValue($0) } + ) + } +} diff --git a/Tests/AuthTests/SessionManagerTests.swift b/Tests/AuthTests/SessionManagerTests.swift index 9effce57..0d56d123 100644 --- a/Tests/AuthTests/SessionManagerTests.swift +++ b/Tests/AuthTests/SessionManagerTests.swift @@ -36,6 +36,7 @@ final class SessionManagerTests: XCTestCase { ), http: http, api: APIClient(clientID: clientID), + codeVerifierStorage: .mock, sessionStorage: SessionStorage.live(clientID: clientID), sessionManager: SessionManager.live(clientID: clientID) ) diff --git a/Tests/AuthTests/StoredSessionTests.swift b/Tests/AuthTests/StoredSessionTests.swift index 977525b0..e6b834e8 100644 --- a/Tests/AuthTests/StoredSessionTests.swift +++ b/Tests/AuthTests/StoredSessionTests.swift @@ -17,6 +17,7 @@ final class StoredSessionTests: XCTestCase { ), http: HTTPClientMock(), api: .init(clientID: clientID), + codeVerifierStorage: .mock, sessionStorage: .live(clientID: clientID), sessionManager: .live(clientID: clientID) )