diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index 72def3a42d..ac68d616b7 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -371,6 +371,7 @@ 478F6FA623C4521F00054A53 /* MoreReviewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478F6FA523C4521F00054A53 /* MoreReviewsViewController.swift */; }; 478F6FA823C5067C00054A53 /* MyReviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478F6FA723C5067C00054A53 /* MyReviewView.swift */; }; 479603732446F17C00F3BDD0 /* User+AppleId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 479603722446F17C00F3BDD0 /* User+AppleId.swift */; }; + 4796037524482E3900F3BDD0 /* KeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4796037424482E3800F3BDD0 /* KeychainStorage.swift */; }; 479D305722C627CB00D18278 /* PartnerBannerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 479D305522C627CB00D18278 /* PartnerBannerViewController.xib */; }; 479D305B22C62F4000D18278 /* MWMBookmarksBannerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 479D305922C62F4000D18278 /* MWMBookmarksBannerViewController.xib */; }; 479D306522C664CE00D18278 /* MWMDownloadBannerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 479D306422C664CE00D18278 /* MWMDownloadBannerViewController.m */; }; @@ -1474,6 +1475,7 @@ 478F6FA523C4521F00054A53 /* MoreReviewsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreReviewsViewController.swift; sourceTree = ""; }; 478F6FA723C5067C00054A53 /* MyReviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyReviewView.swift; sourceTree = ""; }; 479603722446F17C00F3BDD0 /* User+AppleId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "User+AppleId.swift"; sourceTree = ""; }; + 4796037424482E3800F3BDD0 /* KeychainStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStorage.swift; sourceTree = ""; }; 479D305522C627CB00D18278 /* PartnerBannerViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PartnerBannerViewController.xib; sourceTree = ""; }; 479D305922C62F4000D18278 /* MWMBookmarksBannerViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MWMBookmarksBannerViewController.xib; sourceTree = ""; }; 479D306322C664CE00D18278 /* MWMDownloadBannerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMDownloadBannerViewController.h; sourceTree = ""; }; @@ -3226,6 +3228,7 @@ 34BBD6661F8273350070CA50 /* AuthorizationViewController.xib */, 47FA14D0230D52FC003DA979 /* PhoneNumberAuthorizationViewController.swift */, 479603722446F17C00F3BDD0 /* User+AppleId.swift */, + 4796037424482E3800F3BDD0 /* KeychainStorage.swift */, ); path = Authorization; sourceTree = ""; @@ -5835,6 +5838,7 @@ F5BD255A0838E70EC012748E /* DiscoverySearchCell.swift in Sources */, 47C7F9732191E15A00C2760C /* InAppBilling.swift in Sources */, F5BD2CA4DBEFACBC48195F39 /* DiscoveryCollectionHolderCell.swift in Sources */, + 4796037524482E3900F3BDD0 /* KeychainStorage.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/iphone/Maps/UI/Authorization/AuthorizationViewController.swift b/iphone/Maps/UI/Authorization/AuthorizationViewController.swift index cad1cce698..d901c4921c 100644 --- a/iphone/Maps/UI/Authorization/AuthorizationViewController.swift +++ b/iphone/Maps/UI/Authorization/AuthorizationViewController.swift @@ -242,8 +242,7 @@ final class AuthorizationViewController: MWMViewController { private func process(token: String, type: SocialTokenType, firstName: String = "", - lastName: String = "", - completion: ((_ success: Bool) -> Void)? = nil) { + lastName: String = "") { Statistics.logEvent(kStatUGCReviewAuthExternalRequestSuccess, withParameters: [kStatProvider: getProviderStatStr(type: type)]) User.authenticate(withToken: token, @@ -254,7 +253,6 @@ final class AuthorizationViewController: MWMViewController { firstName: firstName, lastName: lastName, source: sourceComponent) { success in - completion?(success) self.logStats(type: type, success: success) if success { self.successHandler?(type) @@ -325,14 +323,14 @@ extension AuthorizationViewController: ASAuthorizationControllerDelegate { switch authorization.credential { case let appleIDCredential as ASAuthorizationAppleIDCredential: guard let token = appleIDCredential.identityToken, - let tokenString = String(data: token, encoding: .utf8), - let fullName = appleIDCredential.fullName else { return } - let appleId = appleIDCredential.user - process(token: tokenString, type: .apple, firstName: fullName.givenName ?? "", lastName: fullName.familyName ?? "") { - if $0 { - User.setAppleId(appleId) - } - } + let tokenString = String(data: token, encoding: .utf8) else { return } + let fullName = appleIDCredential.fullName + let userId = appleIDCredential.user + let appleId = User.getAppleId() + let firstName = fullName?.givenName ?? appleId?.firstName ?? "" + let lastName = fullName?.familyName ?? appleId?.lastName ?? "" + User.setAppleId(AppleId(userId: userId, firstName: firstName, lastName: lastName)) + process(token: tokenString, type: .apple, firstName: firstName, lastName: lastName) default: break } diff --git a/iphone/Maps/UI/Authorization/KeychainStorage.swift b/iphone/Maps/UI/Authorization/KeychainStorage.swift new file mode 100644 index 0000000000..d67c4bf136 --- /dev/null +++ b/iphone/Maps/UI/Authorization/KeychainStorage.swift @@ -0,0 +1,57 @@ +import Foundation + +class KeychainStorage { + static var shared = { + KeychainStorage() + }() + + private init() { } + + func set(_ value: String, forKey key: String) { + var query = self.query(forKey: key) + + if let _ = string(forKey: key) { + var attributesToUpdate = [String: AnyObject]() + attributesToUpdate[kSecValueData as String] = value.data(using: .utf8) as AnyObject + SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary) + return + } + + query[kSecValueData as String] = value.data(using: .utf8) as AnyObject + SecItemAdd(query as CFDictionary, nil) + } + + func string(forKey key: String) -> String? { + var query = self.query(forKey: key) + query[kSecMatchLimit as String] = kSecMatchLimitOne + query[kSecReturnAttributes as String] = kCFBooleanTrue + query[kSecReturnData as String] = kCFBooleanTrue + + var keychainData: AnyObject? + let status = withUnsafeMutablePointer(to: &keychainData) { + SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) + } + + guard status == noErr, + let keychainItem = keychainData as? [String: AnyObject], + let stringData = keychainItem[kSecValueData as String] as? Data, + let string = String(data: stringData, encoding: String.Encoding.utf8) else { + return nil + } + + return string + } + + func deleteString(forKey key: String) { + let query = self.query(forKey: key) + SecItemDelete(query as CFDictionary) + } + + private func query(forKey key: String) -> [String: AnyObject] { + var query = [String: AnyObject]() + query[kSecClass as String] = kSecClassGenericPassword + query[kSecAttrService as String] = "com.mapswithme.full" as AnyObject + query[kSecAttrAccount as String] = key as AnyObject + return query + } +} diff --git a/iphone/Maps/UI/Authorization/User+AppleId.swift b/iphone/Maps/UI/Authorization/User+AppleId.swift index 4f93c8f138..ca38e75a2c 100644 --- a/iphone/Maps/UI/Authorization/User+AppleId.swift +++ b/iphone/Maps/UI/Authorization/User+AppleId.swift @@ -2,22 +2,43 @@ import Foundation fileprivate enum Const { static let kAppleIdKey = "kAppleIdKey" + static let kAppleIdFirstName = "kAppleIdFirstName" + static let kAppleIdLastName = "kAppleIdLastName" +} + +struct AppleId { + let userId: String + let firstName: String + let lastName: String } @available(iOS 13.0, *) extension User { - static func setAppleId(_ appleId: String) { - UserDefaults.standard.set(appleId, forKey: Const.kAppleIdKey) + static func setAppleId(_ appleId: AppleId) { + KeychainStorage.shared.set(appleId.userId, forKey: Const.kAppleIdKey) + KeychainStorage.shared.set(appleId.firstName, forKey: Const.kAppleIdFirstName) + KeychainStorage.shared.set(appleId.lastName, forKey: Const.kAppleIdLastName) + } + + static func getAppleId() -> AppleId? { + guard let userId = KeychainStorage.shared.string(forKey: Const.kAppleIdKey), + let firstName = KeychainStorage.shared.string(forKey: Const.kAppleIdFirstName), + let lastName = KeychainStorage.shared.string(forKey: Const.kAppleIdLastName) else { + return nil + } + return AppleId(userId: userId, firstName: firstName, lastName: lastName) } @objc static func verifyAppleId() { - guard let appleId = UserDefaults.standard.string(forKey: Const.kAppleIdKey) else { return } + guard let userId = KeychainStorage.shared.string(forKey: Const.kAppleIdKey) else { return } let appleIDProvider = ASAuthorizationAppleIDProvider() - appleIDProvider.getCredentialState(forUserID: appleId) { (state, error) in + appleIDProvider.getCredentialState(forUserID: userId) { (state, error) in switch state { case .revoked, .notFound: logOut() - UserDefaults.standard.set(nil, forKey: Const.kAppleIdKey) + KeychainStorage.shared.deleteString(forKey: Const.kAppleIdKey) + KeychainStorage.shared.deleteString(forKey: Const.kAppleIdFirstName) + KeychainStorage.shared.deleteString(forKey: Const.kAppleIdLastName) default: break }