From 1482265280254169196f7e19dcf8d01d9366fcbf Mon Sep 17 00:00:00 2001 From: Ioan Moldovan Date: Mon, 4 Nov 2024 02:44:05 -0600 Subject: [PATCH] #2573 Upgrade GTMAppAuth to v4 (#2633) * feat: upgraded GTMAppAuth to v4 * fix: unit test --- FlowCrypt.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 10 ++--- .../Compose/ComposeViewController.swift | 2 +- .../ComposeViewController+Contacts.swift | 2 +- .../ContactsProviderType.swift | 2 +- .../GoogleContactsProvider.swift | 4 +- .../Mail Provider/Gmail/GmailService.swift | 2 +- .../GoogleAuthManager.swift | 38 ++++++++++--------- .../Mail Provider/GmailServiceTest.swift | 2 +- 9 files changed, 34 insertions(+), 30 deletions(-) diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index 2f867087b..d629b643e 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -3887,7 +3887,7 @@ repositoryURL = "https://github.com/google/GTMAppAuth"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 2.0.0; + minimumVersion = 4.0.0; }; }; 51A1A12629070CDF007F1188 /* XCRemoteSwiftPackageReference "Texture" */ = { diff --git a/FlowCrypt.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/FlowCrypt.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index e2ab48bfe..6029b7795 100644 --- a/FlowCrypt.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/FlowCrypt.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "672e29829148463333cfe67147f4218035ae9dc45722e5edd9d0f3ce2cf783a7", + "originHash" : "05430bf1fc71f707204c86e319466dfc225d7e8224d4328e53f7059acda7dbd3", "pins" : [ { "identity" : "appauth-ios", @@ -42,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/GoogleSignIn-iOS", "state" : { - "revision" : "7932d33686c1dc4d7df7a919aae47361d1cdfda4", - "version" : "7.0.0" + "revision" : "a7965d134c5d3567026c523e0a8a583f73b62b0d", + "version" : "7.1.0" } }, { @@ -60,8 +60,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/GTMAppAuth.git", "state" : { - "revision" : "cee3c709307912d040bd1e06ca919875a92339c6", - "version" : "2.0.0" + "revision" : "5d7d66f647400952b1758b230e019b07c0b4b22a", + "version" : "4.1.1" } }, { diff --git a/FlowCrypt/Controllers/Compose/ComposeViewController.swift b/FlowCrypt/Controllers/Compose/ComposeViewController.swift index 0d458269c..f6236facd 100644 --- a/FlowCrypt/Controllers/Compose/ComposeViewController.swift +++ b/FlowCrypt/Controllers/Compose/ComposeViewController.swift @@ -121,7 +121,7 @@ final class ComposeViewController: TableNodeViewController { self.googleAuthManager = GoogleAuthManager( appDelegateGoogleSessionContainer: UIApplication.shared.delegate as? AppDelegate ) - self.contactsProvider = GoogleContactsProvider(authorization: self.googleAuthManager.authorization(for: appContext.user.email)) + self.contactsProvider = try GoogleContactsProvider(authorization: self.googleAuthManager.authorization(for: appContext.user.email)) let draftsApiClient = try appContext.getRequiredMailProvider().draftsApiClient diff --git a/FlowCrypt/Controllers/Compose/Extensions/ComposeViewController+Contacts.swift b/FlowCrypt/Controllers/Compose/Extensions/ComposeViewController+Contacts.swift index 41a1d76bd..cdff39d11 100644 --- a/FlowCrypt/Controllers/Compose/Extensions/ComposeViewController+Contacts.swift +++ b/FlowCrypt/Controllers/Compose/Extensions/ComposeViewController+Contacts.swift @@ -18,7 +18,7 @@ extension ComposeViewController { for: .gmailLogin(self), appContext: appContext ) - contactsProvider.authorization = googleAuthManager.authorization(for: appContext.user.email) + contactsProvider.authorization = try googleAuthManager.authorization(for: appContext.user.email) shouldEvaluateRecipientInput = true reload(sections: [.contacts]) } catch { diff --git a/FlowCrypt/Functionality/Mail Provider/Contacts Provider/ContactsProviderType.swift b/FlowCrypt/Functionality/Mail Provider/Contacts Provider/ContactsProviderType.swift index c3ec41054..bc96cb6e2 100644 --- a/FlowCrypt/Functionality/Mail Provider/Contacts Provider/ContactsProviderType.swift +++ b/FlowCrypt/Functionality/Mail Provider/Contacts Provider/ContactsProviderType.swift @@ -10,7 +10,7 @@ import Foundation import GTMAppAuth protocol ContactsProviderType { - var authorization: GTMAppAuthFetcherAuthorization? { get set } + var authorization: GTMAppAuth.AuthSession? { get set } var isContactsScopeEnabled: Bool { get } func searchContacts(query: String) async throws -> [Recipient] } diff --git a/FlowCrypt/Functionality/Mail Provider/Contacts Provider/GoogleContactsProvider.swift b/FlowCrypt/Functionality/Mail Provider/Contacts Provider/GoogleContactsProvider.swift index b88715714..e2eac5393 100644 --- a/FlowCrypt/Functionality/Mail Provider/Contacts Provider/GoogleContactsProvider.swift +++ b/FlowCrypt/Functionality/Mail Provider/Contacts Provider/GoogleContactsProvider.swift @@ -18,7 +18,7 @@ enum ContactsProviderError: Error { } class GoogleContactsProvider: ContactsProviderType { - var authorization: GTMAppAuthFetcherAuthorization? { + var authorization: GTMAppAuth.AuthSession? { didSet { guard isContactsScopeEnabled else { return } runWarmupQuery() @@ -68,7 +68,7 @@ class GoogleContactsProvider: ContactsProviderType { } } - init(authorization: GTMAppAuthFetcherAuthorization?) { + init(authorization: GTMAppAuth.AuthSession?) { self.authorization = authorization } diff --git a/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift b/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift index ed40e5c2c..a199f359b 100644 --- a/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift +++ b/FlowCrypt/Functionality/Mail Provider/Gmail/GmailService.swift @@ -29,7 +29,7 @@ class GmailService: MailServiceProvider { self?.progressHandler?(progress) } - guard let authorization = googleAuthManager.authorization(for: currentUserEmail) else { + guard let authorization = try? googleAuthManager.authorization(for: currentUserEmail) else { logger.logWarning("authorization for current user is nil") return service } diff --git a/FlowCrypt/Functionality/Services/Google User Service/GoogleAuthManager.swift b/FlowCrypt/Functionality/Services/Google User Service/GoogleAuthManager.swift index 5c2600088..e1722d419 100644 --- a/FlowCrypt/Functionality/Services/Google User Service/GoogleAuthManager.swift +++ b/FlowCrypt/Functionality/Services/Google User Service/GoogleAuthManager.swift @@ -39,7 +39,7 @@ enum GoogleAuthManagerError: Error, CustomStringConvertible { } protocol GoogleAuthManagerType { - func authorization(for email: String?) -> GTMAppAuthFetcherAuthorization? + func authorization(for email: String?) throws -> GTMAppAuth.AuthSession? } // this is here so that we don't have to include AppDelegate in test target @@ -64,16 +64,17 @@ final class GoogleAuthManager: NSObject, GoogleAuthManagerType { lazy var logger = Logger.nested(in: Self.self, with: .userAppStart) - private func idToken(for email: String?) -> String? { - return authorization(for: email)?.authState.lastTokenResponse?.idToken + private func idToken(for email: String?) throws -> String? { + return try authorization(for: email)?.authState.lastTokenResponse?.idToken } - func authorization(for email: String?) -> GTMAppAuthFetcherAuthorization? { + func authorization(for email: String?) throws -> GTMAppAuth.AuthSession? { guard let email else { return nil } + let keychainStore = GTMAppAuth.KeychainStore(itemName: Constants.index + email) // get authorization from keychain - return GTMAppAuthFetcherAuthorization(fromKeychainForName: Constants.index + email) + return try keychainStore.retrieveAuthSession() } private var authorizationConfiguration: OIDServiceConfiguration { @@ -83,7 +84,7 @@ final class GoogleAuthManager: NSObject, GoogleAuthManagerType { tokenEndpoint: URL(string: "\(GeneralConstants.Mock.backendUrl)/token")! ) } else { - return GTMAppAuthFetcherAuthorization.configurationForGoogle() + return GTMAppAuth.AuthSession.configurationForGoogle() } } } @@ -129,7 +130,8 @@ extension GoogleAuthManager { func signOut(user email: String) { DispatchQueue.main.async { self.appDelegateGoogleSessionContainer?.googleAuthSession = nil - GTMAppAuthFetcherAuthorization.removeFromKeychain(forName: Constants.index + email) + let keychainStore = GTMAppAuth.KeychainStore(itemName: Constants.index + email) + try? keychainStore.removeAuthSession() } } @@ -163,7 +165,7 @@ extension GoogleAuthManager { scopes: [GoogleScope], userEmail: String? ) async throws -> SessionType { - let authorization = GTMAppAuthFetcherAuthorization(authState: authState) + let authorization = GTMAppAuth.AuthSession(authState: authState) guard let email = authorization.userEmail else { throw GoogleAuthManagerError.inconsistentState("Missing email") @@ -178,7 +180,7 @@ extension GoogleAuthManager { email: authorization.userEmail ) } - saveAuth(state: authState, for: email) + try saveAuth(state: authState, for: email) guard let token = authState.lastTokenResponse?.accessToken else { throw GoogleAuthManagerError.inconsistentState("Missing token") } @@ -210,14 +212,16 @@ extension GoogleAuthManager { } // save auth session to keychain - private func saveAuth(state: OIDAuthState, for email: String) { + private func saveAuth(state: OIDAuthState, for email: String) throws { state.stateChangeDelegate = self - let authorization = GTMAppAuthFetcherAuthorization(authState: state) - GTMAppAuthFetcherAuthorization.save(authorization, toKeychainForName: Constants.index + email) + let authorization = GTMAppAuth.AuthSession(authState: state) + let keychainStore = GTMAppAuth.KeychainStore(itemName: Constants.index + email) + + try keychainStore.save(authSession: authorization) } private func fetchGoogleUser( - with authorization: GTMAppAuthFetcherAuthorization + with authorization: GTMAppAuth.AuthSession ) async throws -> GTLROauth2_Userinfo { return try await withCheckedThrowingContinuation { continuation in let query = GTLROauth2Query_UserinfoGet.query() @@ -249,7 +253,7 @@ extension GoogleAuthManager { // MARK: - Tokens extension GoogleAuthManager { func getCachedOrRefreshedIdToken(minExpiryDuration: Double = 0, email: String?) async throws -> String { - guard let idToken = idToken(for: email) else { throw (IdTokenError.missingToken) } + guard let idToken = try idToken(for: email) else { throw (IdTokenError.missingToken) } let decodedToken = try decode(idToken: idToken) @@ -282,7 +286,7 @@ extension GoogleAuthManager { private func performTokenRefresh(email: String?) async throws -> (accessToken: String, idToken: String) { return try await withCheckedThrowingContinuation { continuation in - let authorization = authorization(for: email) + let authorization = try? authorization(for: email) authorization?.authState.setNeedsTokenRefresh() authorization?.authState.performAction { accessToken, idToken, error in guard let accessToken, let idToken else { @@ -299,10 +303,10 @@ extension GoogleAuthManager { // MARK: - OIDAuthStateChangeDelegate extension GoogleAuthManager: OIDAuthStateChangeDelegate { func didChange(_ state: OIDAuthState) { - let authorization = GTMAppAuthFetcherAuthorization(authState: state) + let authorization = GTMAppAuth.AuthSession(authState: state) guard let email = authorization.userEmail else { return } - saveAuth(state: state, for: email) + try? saveAuth(state: state, for: email) } } diff --git a/FlowCryptAppTests/Functionality/Mail Provider/GmailServiceTest.swift b/FlowCryptAppTests/Functionality/Mail Provider/GmailServiceTest.swift index ff5b3cf1d..71616c390 100644 --- a/FlowCryptAppTests/Functionality/Mail Provider/GmailServiceTest.swift +++ b/FlowCryptAppTests/Functionality/Mail Provider/GmailServiceTest.swift @@ -38,7 +38,7 @@ class GmailServiceTest: XCTestCase { // MARK: - Mock class GoogleAuthManagerMock: GoogleAuthManagerType { - func authorization(for email: String?) -> GTMAppAuthFetcherAuthorization? { + func authorization(for email: String?) -> GTMAppAuth.AuthSession? { return nil }