From 863a27f29bb33928121ea9c8f02a711ab074c42d Mon Sep 17 00:00:00 2001 From: aerych Date: Mon, 4 Sep 2017 16:48:05 -0500 Subject: [PATCH 01/13] Login: First pass at service and networking calls for google login. --- .../Services/WordPressComOAuthClientFacade.m | 11 ++++ .../WordPressComOAuthClient.swift | 64 ++++++++++++++++--- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/WordPress/Classes/Services/WordPressComOAuthClientFacade.m b/WordPress/Classes/Services/WordPressComOAuthClientFacade.m index d0abc67030f4..63543510e323 100644 --- a/WordPress/Classes/Services/WordPressComOAuthClientFacade.m +++ b/WordPress/Classes/Services/WordPressComOAuthClientFacade.m @@ -33,4 +33,15 @@ - (void)requestOneTimeCodeWithUsername:(NSString *)username [client requestOneTimeCodeWithUsername:username password:password success:success failure:failure]; } + +- (void)authenticateWithGoogleIDToken:(NSString *)IDToken + success:(void (^)(NSString *authToken))success + failure:(void (^)(NSError *error))failure +{ + WordPressComOAuthClient *client = [WordPressComOAuthClient clientWithClientID:ApiCredentials.client secret:ApiCredentials.secret]; + [client authenticateWithIDToken:IDToken success:success failure:failure]; +} + + + @end diff --git a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift index ba0a22006966..ac5635279051 100644 --- a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift +++ b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift @@ -19,19 +19,27 @@ public final class WordPressComOAuthClient: NSObject { public static let WordPressComOAuthErrorDomain = "WordPressComOAuthError" public static let WordPressComOAuthBaseUrl = "https://public-api.wordpress.com/oauth2" + public static let WordPressComSocialLoginUrl = "https://wordpress.com/wp-login.php?action=social-login-endpoint" public static let WordPressComOAuthRedirectUrl = "https://wordpress.com/" - fileprivate let sessionManager: AFHTTPSessionManager = { - let baseURL = URL(string: WordPressComOAuthClient.WordPressComOAuthBaseUrl) - let sessionConfiguration = URLSessionConfiguration.ephemeral - let sessionManager = AFHTTPSessionManager(baseURL: baseURL, sessionConfiguration: sessionConfiguration) + fileprivate let clientID: String + fileprivate let secret: String + + fileprivate let oauth2SessionManager: AFHTTPSessionManager = { + return WordPressComOAuthClient.sessionManager(url: WordPressComOAuthClient.WordPressComOAuthBaseUrl) + }() + + fileprivate let socialSessionManager: AFHTTPSessionManager = { + return WordPressComOAuthClient.sessionManager(url: WordPressComOAuthClient.WordPressComSocialLoginUrl) + }() + + fileprivate class func sessionManager(url: String) -> AFHTTPSessionManager { + let baseURL = URL(string: url) + let sessionManager = AFHTTPSessionManager(baseURL: baseURL, sessionConfiguration: .ephemeral) sessionManager.responseSerializer = WordPressComOAuthResponseSerializer() sessionManager.requestSerializer.setValue("application/json", forHTTPHeaderField: "Accept") return sessionManager - }() - - fileprivate let clientID: String - fileprivate let secret: String + } /// Creates a WordPresComOAuthClient initialized with the clientID and secret constants defined in the /// ApiCredentials singleton @@ -79,7 +87,7 @@ public final class WordPressComOAuthClient: NSObject { parameters["wpcom_otp"] = multifactorCode as AnyObject? } - sessionManager.post("token", parameters: parameters, progress: nil, success: { (task, responseObject) in + oauth2SessionManager.post("token", parameters: parameters, progress: nil, success: { (task, responseObject) in DDLogVerbose("Received OAuth2 response: \(self.cleanedUpResponseForLogging(responseObject as AnyObject? ?? "nil" as AnyObject))") guard let responseDictionary = responseObject as? [String: AnyObject], let authToken = responseDictionary["access_token"] as? String else { @@ -115,7 +123,7 @@ public final class WordPressComOAuthClient: NSObject { "wpcom_resend_otp": true ] as [String : Any] - sessionManager.post("token", parameters: parameters, progress: nil, success: { (task, responseObject) in + oauth2SessionManager.post("token", parameters: parameters, progress: nil, success: { (task, responseObject) in success() }, failure: { (task, error) in failure(error as NSError) @@ -123,6 +131,42 @@ public final class WordPressComOAuthClient: NSObject { ) } + /// Authenticate on WordPress.com with a social service's ID token. + /// Only google is supported at this time. + /// + /// - Parameters: + /// - token: A social ID token obtained from a supported social service. + /// - success: block to be called if authentication was successful. The OAuth2 token is passed as a parameter. + /// - failure: block to be called if authentication failed. The error object is passed as a parameter. + /// + public func authenticateWithIDToken(_ token: String, + success: @escaping (_ authToken: String?) -> (), + failure: @escaping (_ error: NSError) -> () ) { + let parameters = [ + "client_id": clientID, + "client_secret": secret, + "service": "google", + "get_bearer_token": true, + "id_token" : token, + ] as [String : Any] + + // Passes an empty string for the + socialSessionManager.post("", parameters: parameters, progress: nil, success: { (task, responseObject) in + DDLogVerbose("Received Social Login Oauth response: \(self.cleanedUpResponseForLogging(responseObject as AnyObject? ?? "nil" as AnyObject))") + guard let responseDictionary = responseObject as? [String: AnyObject], + let data = responseDictionary["data"] as? [String: AnyObject], + let authToken = data["bearer_token"] as? String else { + success(nil) + return + } + success(authToken) + + }, failure: { (task, error) in + failure(error as NSError) + } + ) + } + fileprivate func cleanedUpResponseForLogging(_ response: AnyObject) -> AnyObject { guard var responseDictionary = response as? [String: AnyObject], let _ = responseDictionary["access_token"] From da40ded7335b9b895560e14d90ec4fbd746910a2 Mon Sep 17 00:00:00 2001 From: aerych Date: Tue, 5 Sep 2017 18:52:56 -0500 Subject: [PATCH 02/13] Login: First pass at support for google login via LoginFacade --- WordPress/Classes/Services/LoginFacade.h | 9 +++++++++ WordPress/Classes/Services/LoginFacade.m | 19 ++++++++++++++++++- .../Services/WordPressComOAuthClientFacade.h | 4 ++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/WordPress/Classes/Services/LoginFacade.h b/WordPress/Classes/Services/LoginFacade.h index 97de907d3305..1f158fa94a9e 100644 --- a/WordPress/Classes/Services/LoginFacade.h +++ b/WordPress/Classes/Services/LoginFacade.h @@ -113,5 +113,14 @@ */ - (void)finishedLoginWithUsername:(NSString *)username authToken:(NSString *)authToken requiredMultifactorCode:(BOOL)requiredMultifactorCode; + +/** + * Called when finished logging in to a WordPress.com site via a Google token. + * + * @param googleIDToken the token used + * @param authToken authToken to be used to access the site + */ +- (void)finishedLoginWithGoogleIDToken:(NSString *)googleIDToken authToken:(NSString *)authToken; + @end diff --git a/WordPress/Classes/Services/LoginFacade.m b/WordPress/Classes/Services/LoginFacade.m index 414c26f18481..02c8ac6ec8c0 100644 --- a/WordPress/Classes/Services/LoginFacade.m +++ b/WordPress/Classes/Services/LoginFacade.m @@ -52,7 +52,6 @@ - (void)loginWithLoginFields:(LoginFields *)loginFields } } - - (void)requestOneTimeCodeWithLoginFields:(LoginFields *)loginFields { [self.wordpressComOAuthClientFacade requestOneTimeCodeWithUsername:loginFields.username password:loginFields.password success:^{ @@ -62,6 +61,24 @@ - (void)requestOneTimeCodeWithLoginFields:(LoginFields *)loginFields }]; } +- (void)loginToWordPressDotComWithGoogleIDToken:(NSString *)googleIDToken +{ + if ([self.delegate respondsToSelector:@selector(displayLoginMessage:)]) { + [self.delegate displayLoginMessage:NSLocalizedString(@"Connecting to WordPress.com", nil)]; + } + + [self.wordpressComOAuthClientFacade authenticateWithGoogleIDToken:googleIDToken success:^(NSString *authToken) { + if ([self.delegate respondsToSelector:@selector(finishedLoginWithGoogleIDToken:authToken:)]) { + [self.delegate finishedLoginWithGoogleIDToken:googleIDToken authToken:authToken]; + } + } failure:^(NSError *error) { + [WPAppAnalytics track:WPAnalyticsStatLoginFailed error:error]; + if ([self.delegate respondsToSelector:@selector(displayRemoteError:)]) { + [self.delegate displayRemoteError:error]; + } + }]; +} + - (void)signInToWordpressDotCom:(LoginFields *)loginFields { if ([self.delegate respondsToSelector:@selector(displayLoginMessage:)]) { diff --git a/WordPress/Classes/Services/WordPressComOAuthClientFacade.h b/WordPress/Classes/Services/WordPressComOAuthClientFacade.h index 01a74d7d8311..41db5242c739 100644 --- a/WordPress/Classes/Services/WordPressComOAuthClientFacade.h +++ b/WordPress/Classes/Services/WordPressComOAuthClientFacade.h @@ -15,6 +15,10 @@ success:(void (^)(void))success failure:(void (^)(NSError *error))failure; +- (void)authenticateWithGoogleIDToken:(NSString *)IDToken + success:(void (^)(NSString *authToken))success + failure:(void (^)(NSError *error))failure; + @end @interface WordPressComOAuthClientFacade : NSObject From a5f0bb8ef518556d670ec3359936c0b17b86b3ef Mon Sep 17 00:00:00 2001 From: aerych Date: Sun, 10 Sep 2017 20:59:47 -0500 Subject: [PATCH 03/13] Login: Adds method to header. --- WordPress/Classes/Services/LoginFacade.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WordPress/Classes/Services/LoginFacade.h b/WordPress/Classes/Services/LoginFacade.h index 1f158fa94a9e..516b937c7137 100644 --- a/WordPress/Classes/Services/LoginFacade.h +++ b/WordPress/Classes/Services/LoginFacade.h @@ -43,6 +43,8 @@ */ - (void)requestOneTimeCodeWithLoginFields:(LoginFields *)loginFields; +- (void)loginToWordPressDotComWithGoogleIDToken:(NSString *)googleIDToken; + /** * A delegate with a few methods that indicate various aspects of the login process */ From 44d8f1e0cbfbd11f067682e8ae66b89fe86662c5 Mon Sep 17 00:00:00 2001 From: aerych Date: Sun, 17 Sep 2017 09:27:54 -0700 Subject: [PATCH 04/13] Login: Support 2fa with social logins. --- WordPress/Classes/Services/LoginFacade.h | 10 ++ WordPress/Classes/Services/LoginFacade.m | 5 +- .../Services/WordPressComOAuthClientFacade.h | 10 +- .../Services/WordPressComOAuthClientFacade.m | 17 ++- .../WordPressKit.xcodeproj/project.pbxproj | 4 + .../SocialLogin2FANonceInfo.swift | 11 ++ .../WordPressComOAuthClient.swift | 124 +++++++++++++++++- 7 files changed, 168 insertions(+), 13 deletions(-) create mode 100644 WordPressKit/WordPressKit/SocialLogin2FANonceInfo.swift diff --git a/WordPress/Classes/Services/LoginFacade.h b/WordPress/Classes/Services/LoginFacade.h index 516b937c7137..61d0ec6bb3d3 100644 --- a/WordPress/Classes/Services/LoginFacade.h +++ b/WordPress/Classes/Services/LoginFacade.h @@ -2,6 +2,7 @@ @class LoginFields; +@class SocialLogin2FANonceInfo; @protocol WordPressComOAuthClientFacade; @protocol WordPressXMLRPCAPIFacade; @protocol LoginFacadeDelegate; @@ -43,6 +44,7 @@ */ - (void)requestOneTimeCodeWithLoginFields:(LoginFields *)loginFields; + - (void)loginToWordPressDotComWithGoogleIDToken:(NSString *)googleIDToken; /** @@ -88,6 +90,14 @@ */ - (void)needsMultifactorCode; +/** + * This is called when the initial login failed because we need a 2fa code for a social login. + * + * @param userID the WPCom userID of the user logging in. + * @param nonceInfo an object containing information about available 2fa nonce options. + */ +- (void)needsMultifactorCodeForUserID:(NSInteger)userID andNonceInfo:(SocialLogin2FANonceInfo *)nonceInfo; + /** * This is called when there's been an error and we want to inform the user. * diff --git a/WordPress/Classes/Services/LoginFacade.m b/WordPress/Classes/Services/LoginFacade.m index 02c8ac6ec8c0..50af5357bf50 100644 --- a/WordPress/Classes/Services/LoginFacade.m +++ b/WordPress/Classes/Services/LoginFacade.m @@ -40,7 +40,6 @@ - (void)signInWithLoginFields:(LoginFields *)loginFields } } - - (void)loginWithLoginFields:(LoginFields *)loginFields { NSAssert(self.delegate != nil, @"Must set delegate to use service"); @@ -71,6 +70,10 @@ - (void)loginToWordPressDotComWithGoogleIDToken:(NSString *)googleIDToken if ([self.delegate respondsToSelector:@selector(finishedLoginWithGoogleIDToken:authToken:)]) { [self.delegate finishedLoginWithGoogleIDToken:googleIDToken authToken:authToken]; } + } needsMultiFactor:^(NSInteger userID, SocialLogin2FANonceInfo *nonceInfo){ + if ([self.delegate respondsToSelector:@selector(needsMultifactorCodeForUserID:andNonceInfo:)]) { + [self.delegate needsMultifactorCodeForUserID:userID andNonceInfo:nonceInfo]; + } } failure:^(NSError *error) { [WPAppAnalytics track:WPAnalyticsStatLoginFailed error:error]; if ([self.delegate respondsToSelector:@selector(displayRemoteError:)]) { diff --git a/WordPress/Classes/Services/WordPressComOAuthClientFacade.h b/WordPress/Classes/Services/WordPressComOAuthClientFacade.h index 41db5242c739..9b8dd59102be 100644 --- a/WordPress/Classes/Services/WordPressComOAuthClientFacade.h +++ b/WordPress/Classes/Services/WordPressComOAuthClientFacade.h @@ -1,5 +1,6 @@ #import +@class SocialLogin2FANonceInfo; @protocol WordPressComOAuthClientFacade @@ -15,10 +16,17 @@ success:(void (^)(void))success failure:(void (^)(NSError *error))failure; -- (void)authenticateWithGoogleIDToken:(NSString *)IDToken +- (void)authenticateWithGoogleIDToken:(NSString *)token success:(void (^)(NSString *authToken))success + needsMultiFactor:(void (^)(NSInteger userID, SocialLogin2FANonceInfo *nonceInfo))needsMultifactor failure:(void (^)(NSError *error))failure; +- (void)authenticateSocialLoginUser:(NSInteger)userID + authType:(NSString *)authType + twoStepCode:(NSString *)twoStepCode + twoStepNonce:(NSString *)twoStepNonce + success:(void (^)(NSString *authToken))success + failure:(void (^)(NSError *error))failure; @end @interface WordPressComOAuthClientFacade : NSObject diff --git a/WordPress/Classes/Services/WordPressComOAuthClientFacade.m b/WordPress/Classes/Services/WordPressComOAuthClientFacade.m index 63543510e323..a704477da043 100644 --- a/WordPress/Classes/Services/WordPressComOAuthClientFacade.m +++ b/WordPress/Classes/Services/WordPressComOAuthClientFacade.m @@ -33,15 +33,24 @@ - (void)requestOneTimeCodeWithUsername:(NSString *)username [client requestOneTimeCodeWithUsername:username password:password success:success failure:failure]; } - -- (void)authenticateWithGoogleIDToken:(NSString *)IDToken +- (void)authenticateWithGoogleIDToken:(NSString *)token success:(void (^)(NSString *authToken))success + needsMultiFactor:(void (^)(NSInteger userID, SocialLogin2FANonceInfo *nonceInfo))needsMultifactor failure:(void (^)(NSError *error))failure { WordPressComOAuthClient *client = [WordPressComOAuthClient clientWithClientID:ApiCredentials.client secret:ApiCredentials.secret]; - [client authenticateWithIDToken:IDToken success:success failure:failure]; + [client authenticateWithIDToken:token success:success needsMultifactor:needsMultifactor failure:failure]; } - +- (void)authenticateSocialLoginUser:(NSInteger)userID + authType:(NSString *)authType + twoStepCode:(NSString *)twoStepCode + twoStepNonce:(NSString *)twoStepNonce + success:(void (^)(NSString *authToken))success + failure:(void (^)(NSError *error))failure +{ + WordPressComOAuthClient *client = [WordPressComOAuthClient clientWithClientID:ApiCredentials.client secret:ApiCredentials.secret]; + [client authenticateSocialLoginUser:userID authType:authType twoStepCode:twoStepCode twoStepNonce:twoStepNonce success:success failure:failure]; +} @end diff --git a/WordPressKit/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit/WordPressKit.xcodeproj/project.pbxproj index a71bade48bd4..77aa73c4ebec 100644 --- a/WordPressKit/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit/WordPressKit.xcodeproj/project.pbxproj @@ -336,6 +336,7 @@ E13EE1491F332B8500C15787 /* site-plugins-success.json in Resources */ = {isa = PBXBuildFile; fileRef = E13EE1481F332B8500C15787 /* site-plugins-success.json */; }; E13EE14C1F332C4400C15787 /* PluginServiceRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13EE14B1F332C4400C15787 /* PluginServiceRemoteTests.swift */; }; E14694031F344F71004052C8 /* site-plugins-error.json in Resources */ = {isa = PBXBuildFile; fileRef = E14694021F344F71004052C8 /* site-plugins-error.json */; }; + E632D7781F6E047400297F6D /* SocialLogin2FANonceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E632D7771F6E047400297F6D /* SocialLogin2FANonceInfo.swift */; }; E6C1E8491EF21FC100D139D9 /* is-passwordless-account-no-account-found.json in Resources */ = {isa = PBXBuildFile; fileRef = E6C1E8471EF21FC100D139D9 /* is-passwordless-account-no-account-found.json */; }; E6C1E84A1EF21FC100D139D9 /* is-passwordless-account-success.json in Resources */ = {isa = PBXBuildFile; fileRef = E6C1E8481EF21FC100D139D9 /* is-passwordless-account-success.json */; }; F8B82B9E92C9637430D7D032 /* Pods_WordPressKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F902B70CBB63DD7EB9D275A /* Pods_WordPressKitTests.framework */; }; @@ -693,6 +694,7 @@ E13EE14B1F332C4400C15787 /* PluginServiceRemoteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PluginServiceRemoteTests.swift; sourceTree = ""; }; E14694021F344F71004052C8 /* site-plugins-error.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "site-plugins-error.json"; sourceTree = ""; }; E5273E8FA8F6117F941D6CC1 /* Pods-WordPressKit.release-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKit.release-internal.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressKit/Pods-WordPressKit.release-internal.xcconfig"; sourceTree = ""; }; + E632D7771F6E047400297F6D /* SocialLogin2FANonceInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocialLogin2FANonceInfo.swift; sourceTree = ""; }; E6C1E8471EF21FC100D139D9 /* is-passwordless-account-no-account-found.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "is-passwordless-account-no-account-found.json"; sourceTree = ""; }; E6C1E8481EF21FC100D139D9 /* is-passwordless-account-success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "is-passwordless-account-success.json"; sourceTree = ""; }; EF1D8EBEFA46779A06F52788 /* Pods-WordPressKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKitTests.release.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressKitTests/Pods-WordPressKitTests.release.xcconfig"; sourceTree = ""; }; @@ -1097,6 +1099,7 @@ 9309995A1F16616A00F006A1 /* RemoteTheme.m */, 93BD27671EE736A8002BB00B /* RemoteUser.h */, 93BD27681EE736A8002BB00B /* RemoteUser.m */, + E632D7771F6E047400297F6D /* SocialLogin2FANonceInfo.swift */, 930F52B91ECF8A44002F921B /* Stats */, ); name = Models; @@ -1775,6 +1778,7 @@ 9368C7C01EC630CE0092CE8E /* StatsStringUtilities.m in Sources */, 74BA04FA1F06DC3900ED5CD8 /* RemoteComment.m in Sources */, 93C674EC1EE8348F00BFAF05 /* RemoteBlogOptionsHelper.m in Sources */, + E632D7781F6E047400297F6D /* SocialLogin2FANonceInfo.swift in Sources */, 7403A2E41EF06ED500DED7DC /* AccountSettingsRemote.swift in Sources */, 93C674E81EE8345300BFAF05 /* RemoteBlog.m in Sources */, 93BD27831EE73944002BB00B /* WordPressRSDParser.swift in Sources */, diff --git a/WordPressKit/WordPressKit/SocialLogin2FANonceInfo.swift b/WordPressKit/WordPressKit/SocialLogin2FANonceInfo.swift new file mode 100644 index 000000000000..d87bd5d13d7f --- /dev/null +++ b/WordPressKit/WordPressKit/SocialLogin2FANonceInfo.swift @@ -0,0 +1,11 @@ +import Foundation + +@objc +public class SocialLogin2FANonceInfo: NSObject { + var nonceSMS = "" + var nonceBackup = "" + var nonceAuthenticator = "" + var supportedAuthTypes = [String]() // backup|authenticator|sms + var notificationSent = "" // none|sms + var phoneNumber = "" // The last two digits of the phone number to which an SMS was sent. +} diff --git a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift index ac5635279051..9fd11f0b59ed 100644 --- a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift +++ b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift @@ -19,8 +19,9 @@ public final class WordPressComOAuthClient: NSObject { public static let WordPressComOAuthErrorDomain = "WordPressComOAuthError" public static let WordPressComOAuthBaseUrl = "https://public-api.wordpress.com/oauth2" - public static let WordPressComSocialLoginUrl = "https://wordpress.com/wp-login.php?action=social-login-endpoint" + public static let WordPressComSocialLoginUrl = "https://wordpress.com/wp-login.php" public static let WordPressComOAuthRedirectUrl = "https://wordpress.com/" + public static let WordPressComSocialLoginEndpointVersion = 1.0 fileprivate let clientID: String fileprivate let secret: String @@ -140,26 +141,49 @@ public final class WordPressComOAuthClient: NSObject { /// - failure: block to be called if authentication failed. The error object is passed as a parameter. /// public func authenticateWithIDToken(_ token: String, - success: @escaping (_ authToken: String?) -> (), - failure: @escaping (_ error: NSError) -> () ) { + success: @escaping (_ authToken: String?) -> Void, + needsMultifactor: @escaping (_ userID: Int, _ nonceInfo: SocialLogin2FANonceInfo) -> Void, + failure: @escaping (_ error: NSError) -> Void ) { let parameters = [ + "acton": "social-login-endpoint", "client_id": clientID, "client_secret": secret, "service": "google", "get_bearer_token": true, "id_token" : token, + "version": WordPressComOAuthClient.WordPressComSocialLoginEndpointVersion, ] as [String : Any] // Passes an empty string for the socialSessionManager.post("", parameters: parameters, progress: nil, success: { (task, responseObject) in DDLogVerbose("Received Social Login Oauth response: \(self.cleanedUpResponseForLogging(responseObject as AnyObject? ?? "nil" as AnyObject))") + + let defaultError = NSError(domain: WordPressComOAuthClient.WordPressComOAuthErrorDomain, + code: WordPressComOAuthError.unknown.rawValue, + userInfo: nil) + + // Make sure we received expected data. guard let responseDictionary = responseObject as? [String: AnyObject], - let data = responseDictionary["data"] as? [String: AnyObject], - let authToken = data["bearer_token"] as? String else { - success(nil) + let data = responseDictionary["data"] as? [String: AnyObject] else { + failure(defaultError) return } - success(authToken) + + // Check for a bearer token. If one is found then we're authed. + if let authToken = data["bearer_token"] as? String { + success(authToken) + return + } + + // If there is no bearer token, check for 2fa enabled. + guard let userID = data["user_id"] as? Int, + let _ = data["two_step_nonce_backup"] else { + failure(defaultError) + return + } + + let nonceInfo = self.extractNonceInfo(data: data) + needsMultifactor(userID, nonceInfo) }, failure: { (task, error) in failure(error as NSError) @@ -167,6 +191,92 @@ public final class WordPressComOAuthClient: NSObject { ) } + /// A helper method to get an instance of SocialLogin2FANonceInfo and populate + /// it with the supplied data. + /// + /// - Parameters: + /// - data: The dictionary to use to populate the instance. + /// + /// - Return: SocialLogin2FANonceInfo + /// + private func extractNonceInfo(data:[String: AnyObject]) -> SocialLogin2FANonceInfo { + let nonceInfo = SocialLogin2FANonceInfo() + + if let nonceAuthenticator = data["two_step_nonce_authenticator"] as? String { + nonceInfo.nonceAuthenticator = nonceAuthenticator + } + + if let nonce = data["two_step_nonce_sms"] as? String { + nonceInfo.nonceSMS = nonce + } + + if let nonce = data["two_step_nonce_backup"] as? String { + nonceInfo.nonceBackup = nonce + } + + if let notification = data["two_step_notification_sent"] as? String { + nonceInfo.notificationSent = notification + } + + if let authTypes = data["two_step_supported_auth_types"] as? [String] { + nonceInfo.supportedAuthTypes = authTypes + } + + if let phone = data["phone_number"] as? String { + nonceInfo.phoneNumber = phone + } + + return nonceInfo + } + + /// Completes a social login that has 2fa enabled. + /// + /// - Parameters: + /// - userID: The wpcom user id. + /// - authType: The type of 2fa authentication being used. (sms|backup|authenticator) + /// - twoStepCode: The user's 2fa code. + /// - twoStepNonce: The nonce returned from a social login attempt. + /// - success: block to be called if authentication was successful. The OAuth2 token is passed as a parameter. + /// - failure: block to be called if authentication failed. The error object is passed as a parameter. + /// + public func authenticateSocialLoginUser(_ userID: Int, + authType: String, + twoStepCode: String, + twoStepNonce: String, + success: @escaping (_ authToken: String?) -> Void, + failure: @escaping (_ error: NSError) -> Void ) { + let parameters = [ + "acton": "two-step-authentication-endpoint", + "user_id" : userID, + "auth_type" : authType, + "two_step_code": twoStepCode, + "two_step_nonce": twoStepNonce, + "get_bearer_token": true, + "client_id": clientID, + "client_secret": secret, + "version": WordPressComOAuthClient.WordPressComSocialLoginEndpointVersion, + ] as [String : Any] + + socialSessionManager.post("", parameters: parameters, progress: nil, success: { (task, responseObject) in + DDLogVerbose("Received Social Login Oauth response: \(self.cleanedUpResponseForLogging(responseObject as AnyObject? ?? "nil" as AnyObject))") + guard let responseDictionary = responseObject as? [String: AnyObject], + let data = responseDictionary["data"] as? [String: AnyObject], + let authToken = data["bearer_token"] as? String else { + failure(NSError(domain: WordPressComOAuthClient.WordPressComOAuthErrorDomain, + code: WordPressComOAuthError.unknown.rawValue, + userInfo: nil)) + return + } + + success(authToken) + + }, failure: { (task, error) in + failure(error as NSError) + } + ) + } + + fileprivate func cleanedUpResponseForLogging(_ response: AnyObject) -> AnyObject { guard var responseDictionary = response as? [String: AnyObject], let _ = responseDictionary["access_token"] From f6f81a9104438c0db296960eae477398be8957d6 Mon Sep 17 00:00:00 2001 From: aerych Date: Sun, 17 Sep 2017 10:01:14 -0700 Subject: [PATCH 05/13] Login: Adds service methods for connecting social account. --- WordPress/Classes/Services/AccountService.h | 25 ++++++++++++ WordPress/Classes/Services/AccountService.m | 19 +++++++++ .../WordPressKit/AccountServiceRemote.h | 24 +++++++++++ .../WordPressKit/AccountServiceRemoteREST.m | 40 +++++++++++++++++++ 4 files changed, 108 insertions(+) diff --git a/WordPress/Classes/Services/AccountService.h b/WordPress/Classes/Services/AccountService.h index 717b139005fc..6f452cb6864c 100644 --- a/WordPress/Classes/Services/AccountService.h +++ b/WordPress/Classes/Services/AccountService.h @@ -93,6 +93,31 @@ extern NSString *const WPAccountEmailAndDefaultBlogUpdatedNotification; - (void)requestAuthenticationLink:(NSString *)email success:(void (^)())success failure:(void (^)(NSError *error))failure; +/** + Connect an account a social service via an ID token. + + @param service The name of the social service. + @param token The service's OpenID Connect (JWT) ID token for the user. + @param success + @param failure + */ +- (void)connectToSocialService:(NSString *)service + serviceIDToken:(NSString *)token + success:(void (^)())success + failure:(void (^)(NSError *error))failure; + + +/** + Disconnect an account a social service via an ID token. + + @param service The name of the social service. + @param success + @param failure + */ +- (void)disconnectFromSocialService:(NSString *)service + success:(void (^)())success + failure:(void (^)(NSError *error))failure; + ///----------------------- /// @name Account creation ///----------------------- diff --git a/WordPress/Classes/Services/AccountService.m b/WordPress/Classes/Services/AccountService.m index 292d7b59d696..25ac25e63069 100644 --- a/WordPress/Classes/Services/AccountService.m +++ b/WordPress/Classes/Services/AccountService.m @@ -197,6 +197,25 @@ - (void)requestAuthenticationLink:(NSString *)email success:(void (^)())success } +- (void)connectToSocialService:(NSString *)service + serviceIDToken:(NSString *)token + success:(void (^)())success + failure:(void (^)(NSError *error))failure +{ + id remote = [self remoteForAccount:[self defaultWordPressComAccount]]; + [remote connectToSocialService:service serviceIDToken:token success:success failure:failure]; +} + + +- (void)disconnectFromSocialService:(NSString *)service + success:(void (^)())success + failure:(void (^)(NSError *error))failure +{ + id remote = [self remoteForAccount:[self defaultWordPressComAccount]]; + [remote disconnectFromSocialService:service success:success failure:failure]; +} + + ///----------------------- /// @name Account creation ///----------------------- diff --git a/WordPressKit/WordPressKit/AccountServiceRemote.h b/WordPressKit/WordPressKit/AccountServiceRemote.h index caa2c0cb9834..2c091d58978a 100644 --- a/WordPressKit/WordPressKit/AccountServiceRemote.h +++ b/WordPressKit/WordPressKit/AccountServiceRemote.h @@ -81,4 +81,28 @@ wpcomScheme:(NSString *)scheme success:(void (^)())success failure:(void (^)(NSError *error))failure; + +/** + * @brief Connect to the specified social service via its OpenID Connect (JWT) token. + * + * @param service The name of the social service. + * @param token The OpenID Connect (JWT) ID token identifying the user on the social service. + * @param success The block that will be executed on success. Can be nil. + * @param failure The block that will be executed on failure. Can be nil. + */ +- (void)connectToSocialService:(NSString *)service + serviceIDToken:(NSString *)token + success:(void (^)())success + failure:(void (^)(NSError *error))failure; + +/** + * @brief Disconnect fromm the specified social service. + * + * @param service The name of the social service. + * @param success The block that will be executed on success. Can be nil. + * @param failure The block that will be executed on failure. Can be nil. + */ +- (void)disconnectFromSocialService:(NSString *)service + success:(void (^)())success + failure:(void (^)(NSError *error))failure; @end diff --git a/WordPressKit/WordPressKit/AccountServiceRemoteREST.m b/WordPressKit/WordPressKit/AccountServiceRemoteREST.m index e0ea1f040da8..6dd6ea3a36ec 100644 --- a/WordPressKit/WordPressKit/AccountServiceRemoteREST.m +++ b/WordPressKit/WordPressKit/AccountServiceRemoteREST.m @@ -233,6 +233,46 @@ - (void)requestWPComAuthLinkForEmail:(NSString *)email }]; } +- (void)connectToSocialService:(NSString *)service + serviceIDToken:(NSString *)token + success:(void (^)())success + failure:(void (^)(NSError *error))failure +{ + NSString *path = [self pathForEndpoint:@"me/social-login/connect" withVersion:ServiceRemoteWordPressComRESTApiVersion_1_1]; + NSDictionary *params = @{ + @"service":service, + @"id_token":token + }; + [self.wordPressComRestApi POST:path parameters:params success:^(id responseObject, NSHTTPURLResponse *httpResponse) { + if (success) { + success(); + } + } failure:^(NSError *error, NSHTTPURLResponse *httpResponse) { + if (failure) { + failure(error); + } + }]; +} + +- (void)disconnectFromSocialService:(NSString *)service + success:(void (^)())success + failure:(void (^)(NSError *error))failure +{ + NSString *path = [self pathForEndpoint:@"me/social-login/disconnect" withVersion:ServiceRemoteWordPressComRESTApiVersion_1_1]; + NSDictionary *params = @{ + @"service":service + }; + [self.wordPressComRestApi POST:path parameters:params success:^(id responseObject, NSHTTPURLResponse *httpResponse) { + if (success) { + success(); + } + } failure:^(NSError *error, NSHTTPURLResponse *httpResponse) { + if (failure) { + failure(error); + } + }]; +} + #pragma mark - Private Methods From 3f2f53d87f134eedd6f927a63696c88a5b8225db Mon Sep 17 00:00:00 2001 From: aerych Date: Sun, 17 Sep 2017 11:58:27 -0700 Subject: [PATCH 06/13] Login: Tweak endpoint signatures. --- .../WordPressKit/WordPressComOAuthClient.swift | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift index 9fd11f0b59ed..643a4a777352 100644 --- a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift +++ b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift @@ -19,7 +19,8 @@ public final class WordPressComOAuthClient: NSObject { public static let WordPressComOAuthErrorDomain = "WordPressComOAuthError" public static let WordPressComOAuthBaseUrl = "https://public-api.wordpress.com/oauth2" - public static let WordPressComSocialLoginUrl = "https://wordpress.com/wp-login.php" + public static let WordPressComSocialLoginUrl = "https://wordpress.com/wp-login.php?action=social-login-endpoint&version=1.0" + public static let WordPressComSocialLogin2FAUrl = "https://wordpress.com/wp-login.php?action=two-step-authentication-endpoint&version=1.0" public static let WordPressComOAuthRedirectUrl = "https://wordpress.com/" public static let WordPressComSocialLoginEndpointVersion = 1.0 @@ -34,6 +35,10 @@ public final class WordPressComOAuthClient: NSObject { return WordPressComOAuthClient.sessionManager(url: WordPressComOAuthClient.WordPressComSocialLoginUrl) }() + fileprivate let social2FASessionManager: AFHTTPSessionManager = { + return WordPressComOAuthClient.sessionManager(url: WordPressComOAuthClient.WordPressComSocialLogin2FAUrl) + }() + fileprivate class func sessionManager(url: String) -> AFHTTPSessionManager { let baseURL = URL(string: url) let sessionManager = AFHTTPSessionManager(baseURL: baseURL, sessionConfiguration: .ephemeral) @@ -145,13 +150,11 @@ public final class WordPressComOAuthClient: NSObject { needsMultifactor: @escaping (_ userID: Int, _ nonceInfo: SocialLogin2FANonceInfo) -> Void, failure: @escaping (_ error: NSError) -> Void ) { let parameters = [ - "acton": "social-login-endpoint", "client_id": clientID, "client_secret": secret, "service": "google", "get_bearer_token": true, "id_token" : token, - "version": WordPressComOAuthClient.WordPressComSocialLoginEndpointVersion, ] as [String : Any] // Passes an empty string for the @@ -246,7 +249,6 @@ public final class WordPressComOAuthClient: NSObject { success: @escaping (_ authToken: String?) -> Void, failure: @escaping (_ error: NSError) -> Void ) { let parameters = [ - "acton": "two-step-authentication-endpoint", "user_id" : userID, "auth_type" : authType, "two_step_code": twoStepCode, @@ -254,10 +256,9 @@ public final class WordPressComOAuthClient: NSObject { "get_bearer_token": true, "client_id": clientID, "client_secret": secret, - "version": WordPressComOAuthClient.WordPressComSocialLoginEndpointVersion, ] as [String : Any] - socialSessionManager.post("", parameters: parameters, progress: nil, success: { (task, responseObject) in + social2FASessionManager.post("", parameters: parameters, progress: nil, success: { (task, responseObject) in DDLogVerbose("Received Social Login Oauth response: \(self.cleanedUpResponseForLogging(responseObject as AnyObject? ?? "nil" as AnyObject))") guard let responseDictionary = responseObject as? [String: AnyObject], let data = responseDictionary["data"] as? [String: AnyObject], From ad0ae956425de456f3282a3a18d8fde621e3c4e7 Mon Sep 17 00:00:00 2001 From: aerych Date: Thu, 28 Sep 2017 14:47:57 -0500 Subject: [PATCH 07/13] Login: Wires up basic UI support for social login. --- .../NUX/LoginEmailViewController.swift | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift index 0868b6068e75..7b11c19e9147 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift @@ -9,6 +9,7 @@ class LoginEmailViewController: LoginViewController, SigninKeyboardResponder { @IBOutlet var bottomContentConstraint: NSLayoutConstraint? @IBOutlet var verticalCenterConstraint: NSLayoutConstraint? var onePasswordButton: UIButton! + var googleLoginButon: UIButton? var didFindSafariSharedCredentials = false var didRequestSafariSharedCredentials = false @@ -124,7 +125,8 @@ class LoginEmailViewController: LoginViewController, SigninKeyboardResponder { /// Add the log in with Google button to the view func addGoogleButton() { - guard Feature.enabled(.googleLogin) else { + guard Feature.enabled(.googleLogin), + let instructionLabel = instructionLabel else { return } @@ -134,10 +136,11 @@ class LoginEmailViewController: LoginViewController, SigninKeyboardResponder { view.addConstraints([ button.topAnchor.constraint(equalTo: self.emailTextField.bottomAnchor, constant: Constants.googleButtonOffset), - button.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor), - button.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor), + button.leadingAnchor.constraint(equalTo: instructionLabel.leadingAnchor), + button.trailingAnchor.constraint(equalTo:instructionLabel.trailingAnchor), button.centerXAnchor.constraint(equalTo: emailTextField.centerXAnchor) ]) + googleLoginButon = button } func googleLoginTapped() { @@ -172,6 +175,8 @@ class LoginEmailViewController: LoginViewController, SigninKeyboardResponder { /// override func configureViewLoading(_ loading: Bool) { emailTextField.isEnabled = !loading + googleLoginButon?.isEnabled = !loading + submitButton?.isEnabled = !loading submitButton?.showActivityIndicator(loading) } @@ -370,15 +375,37 @@ class LoginEmailViewController: LoginViewController, SigninKeyboardResponder { } +// LoginFacadeDelegate methods for Google Google Sign In +extension LoginEmailViewController { + func finishedLogin(withGoogleIDToken googleIDToken: String!, authToken: String!) { + let username = loginFields.username + syncWPCom(username, authToken: authToken, requiredMultifactor: false) + } + + func needsMultifactorCode(forUserID userID: Int, andNonceInfo nonceInfo: SocialLogin2FANonceInfo!) { + // TODO: to be implemented. + } +} + extension LoginEmailViewController: GIDSignInDelegate { func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) { - // TODO: implement wpcom login via Google code + // TODO: finish implementing wpcom login via Google code + guard let token = user.authentication.idToken, + let email = user.profile.email else { + // The Google SignIn for may have been canceled. + //TODO: Add analytis + return; + } + + // Store the email address. + loginFields.emailAddress = email + loginFields.username = email + + configureViewLoading(true) + + loginFacade.loginToWordPressDotCom(withGoogleIDToken: token) - let alert = UIAlertController(title: "Login Success", message: "Google login succeeded. Actual wpcom account login yet to be implemented.", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "Ok. Thanks.", style: .default, handler: { [weak self] (action) in - self?.dismiss(animated: true){} - })) - present(alert, animated: true) + //TODO: Add analytis } } From 778ed5d7cc69f4a7cc14b960695f43177494192c Mon Sep 17 00:00:00 2001 From: aerych Date: Thu, 28 Sep 2017 14:48:12 -0500 Subject: [PATCH 08/13] Login: Updates Podfile.lock --- Podfile.lock | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index 0f2409e0f3c2..bbc82479e593 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -35,7 +35,27 @@ PODS: - FormatterKit/Resources (1.8.2) - FormatterKit/TimeIntervalFormatter (1.8.2): - FormatterKit/Resources + - GoogleSignIn (4.0.2): + - GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1) + - GoogleToolboxForMac/NSString+URLArguments (~> 2.1) + - GTMOAuth2 (~> 1.0) + - GTMSessionFetcher/Core (~> 1.1) + - GoogleToolboxForMac/DebugUtils (2.1.1): + - GoogleToolboxForMac/Defines (= 2.1.1) + - GoogleToolboxForMac/Defines (2.1.1) + - GoogleToolboxForMac/NSDictionary+URLArguments (2.1.1): + - GoogleToolboxForMac/DebugUtils (= 2.1.1) + - GoogleToolboxForMac/Defines (= 2.1.1) + - GoogleToolboxForMac/NSString+URLArguments (= 2.1.1) + - GoogleToolboxForMac/NSString+URLArguments (2.1.1) - Gridicons (0.10) + - GTMOAuth2 (1.1.5): + - GTMSessionFetcher (~> 1.1) + - GTMSessionFetcher (1.1.11): + - GTMSessionFetcher/Full (= 1.1.11) + - GTMSessionFetcher/Core (1.1.11) + - GTMSessionFetcher/Full (1.1.11): + - GTMSessionFetcher/Core (= 1.1.11) - HockeySDK (4.1.6): - HockeySDK/DefaultLib (= 4.1.6) - HockeySDK/DefaultLib (4.1.6) @@ -112,6 +132,7 @@ DEPENDENCIES: - Expecta (= 1.0.6) - FLAnimatedImage (= 1.0.12) - FormatterKit/TimeIntervalFormatter (= 1.8.2) + - GoogleSignIn (= 4.0.2) - Gridicons (= 0.10) - HockeySDK (= 4.1.6) - lottie-ios (= 1.5.1) @@ -162,7 +183,11 @@ SPEC CHECKSUMS: Fabric: 2fb5676bc811af011a04513451f463dac6803206 FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31 FormatterKit: 4b8f29acc9b872d5d12a63efb560661e8f2e1b98 + GoogleSignIn: d7ad83f13480e1f30453471d00d19b5293ac46af + GoogleToolboxForMac: 8e329f1b599f2512c6b10676d45736bcc2cbbeb0 Gridicons: e661b35e7d4f0ca79e8b2a2072c0081b91b5d1e0 + GTMOAuth2: be83fd28d63ae3087e7d351b1f39c1a7e24ab6e7 + GTMSessionFetcher: 5ad62e8200fa00ed011fe5e08d27fef72c5b1429 HockeySDK: 95db557d54489a570dcdefae0d02f98eecc279a3 lottie-ios: f680a7c4cb7a567ecf258fde0f967913aff111b8 MGSwipeTableCell: 19a1d65dcc1ceaea42eb44bb2adae32101904514 @@ -183,6 +208,6 @@ SPEC CHECKSUMS: WPMediaPicker: 6be379b2f082af41c3ad62d5d3cab7ccc242a644 wpxmlrpc: bfc572f62ce7ee897f6f38b098d2ba08732ecef4 -PODFILE CHECKSUM: 65548e8c0b110beaccca125185c061af6f0041f1 +PODFILE CHECKSUM: 67a882ec3aeda5de8c83ad0d017fe8781642ff1b COCOAPODS: 1.3.1 From e54c0f95efb5fa7dc1c1bff68dcea754b523cda3 Mon Sep 17 00:00:00 2001 From: aerych Date: Thu, 28 Sep 2017 15:09:24 -0500 Subject: [PATCH 09/13] Removes trailing semicolon. --- .../Classes/ViewRelated/NUX/LoginEmailViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift index 7b11c19e9147..11857a509b91 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift @@ -394,7 +394,7 @@ extension LoginEmailViewController: GIDSignInDelegate { let email = user.profile.email else { // The Google SignIn for may have been canceled. //TODO: Add analytis - return; + return } // Store the email address. From 511086c45cf5306509c2171639f8018d10765bf0 Mon Sep 17 00:00:00 2001 From: aerych Date: Fri, 29 Sep 2017 09:29:43 -0500 Subject: [PATCH 10/13] Login: Fix typo. --- .../Classes/ViewRelated/NUX/LoginEmailViewController.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift index 11857a509b91..252a9506616e 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift @@ -9,7 +9,7 @@ class LoginEmailViewController: LoginViewController, SigninKeyboardResponder { @IBOutlet var bottomContentConstraint: NSLayoutConstraint? @IBOutlet var verticalCenterConstraint: NSLayoutConstraint? var onePasswordButton: UIButton! - var googleLoginButon: UIButton? + var googleLoginButton: UIButton? var didFindSafariSharedCredentials = false var didRequestSafariSharedCredentials = false @@ -140,7 +140,7 @@ class LoginEmailViewController: LoginViewController, SigninKeyboardResponder { button.trailingAnchor.constraint(equalTo:instructionLabel.trailingAnchor), button.centerXAnchor.constraint(equalTo: emailTextField.centerXAnchor) ]) - googleLoginButon = button + googleLoginButton = button } func googleLoginTapped() { @@ -175,7 +175,7 @@ class LoginEmailViewController: LoginViewController, SigninKeyboardResponder { /// override func configureViewLoading(_ loading: Bool) { emailTextField.isEnabled = !loading - googleLoginButon?.isEnabled = !loading + googleLoginButton?.isEnabled = !loading submitButton?.isEnabled = !loading submitButton?.showActivityIndicator(loading) From 449d94cfe2f6d76100a9aa3b9ec75c68b0ddc363 Mon Sep 17 00:00:00 2001 From: aerych Date: Fri, 29 Sep 2017 09:34:55 -0500 Subject: [PATCH 11/13] Login: Additional comments. --- WordPressKit/WordPressKit/WordPressComOAuthClient.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift index 643a4a777352..558f695ebf8e 100644 --- a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift +++ b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift @@ -143,6 +143,7 @@ public final class WordPressComOAuthClient: NSObject { /// - Parameters: /// - token: A social ID token obtained from a supported social service. /// - success: block to be called if authentication was successful. The OAuth2 token is passed as a parameter. + /// - needsMultifactor: block to be called if a 2fa token is needed to complete the auth process. /// - failure: block to be called if authentication failed. The error object is passed as a parameter. /// public func authenticateWithIDToken(_ token: String, @@ -157,7 +158,7 @@ public final class WordPressComOAuthClient: NSObject { "id_token" : token, ] as [String : Any] - // Passes an empty string for the + // Passes an empty string for the path. The session manager was composed with the full endpoint path. socialSessionManager.post("", parameters: parameters, progress: nil, success: { (task, responseObject) in DDLogVerbose("Received Social Login Oauth response: \(self.cleanedUpResponseForLogging(responseObject as AnyObject? ?? "nil" as AnyObject))") @@ -258,6 +259,7 @@ public final class WordPressComOAuthClient: NSObject { "client_secret": secret, ] as [String : Any] + // Passes an empty string for the path. The session manager was composed with the full endpoint path. social2FASessionManager.post("", parameters: parameters, progress: nil, success: { (task, responseObject) in DDLogVerbose("Received Social Login Oauth response: \(self.cleanedUpResponseForLogging(responseObject as AnyObject? ?? "nil" as AnyObject))") guard let responseDictionary = responseObject as? [String: AnyObject], From 89c5cc850150a7b3255bd8855e4105b1d5089449 Mon Sep 17 00:00:00 2001 From: aerych Date: Fri, 29 Sep 2017 10:40:06 -0500 Subject: [PATCH 12/13] Login: Renames field --- .../WordPressKit/WordPressComOAuthClient.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift index 558f695ebf8e..65a31bc7224e 100644 --- a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift +++ b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift @@ -168,25 +168,25 @@ public final class WordPressComOAuthClient: NSObject { // Make sure we received expected data. guard let responseDictionary = responseObject as? [String: AnyObject], - let data = responseDictionary["data"] as? [String: AnyObject] else { + let responseData = responseDictionary["data"] as? [String: AnyObject] else { failure(defaultError) return } // Check for a bearer token. If one is found then we're authed. - if let authToken = data["bearer_token"] as? String { + if let authToken = responseData["bearer_token"] as? String { success(authToken) return } // If there is no bearer token, check for 2fa enabled. - guard let userID = data["user_id"] as? Int, - let _ = data["two_step_nonce_backup"] else { + guard let userID = responseData["user_id"] as? Int, + let _ = responseData["two_step_nonce_backup"] else { failure(defaultError) return } - let nonceInfo = self.extractNonceInfo(data: data) + let nonceInfo = self.extractNonceInfo(data: responseData) needsMultifactor(userID, nonceInfo) }, failure: { (task, error) in @@ -263,8 +263,8 @@ public final class WordPressComOAuthClient: NSObject { social2FASessionManager.post("", parameters: parameters, progress: nil, success: { (task, responseObject) in DDLogVerbose("Received Social Login Oauth response: \(self.cleanedUpResponseForLogging(responseObject as AnyObject? ?? "nil" as AnyObject))") guard let responseDictionary = responseObject as? [String: AnyObject], - let data = responseDictionary["data"] as? [String: AnyObject], - let authToken = data["bearer_token"] as? String else { + let responseData = responseDictionary["data"] as? [String: AnyObject], + let authToken = responseData["bearer_token"] as? String else { failure(NSError(domain: WordPressComOAuthClient.WordPressComOAuthErrorDomain, code: WordPressComOAuthError.unknown.rawValue, userInfo: nil)) From acbf134fd4f246f40172f45088764fe7e5a02f28 Mon Sep 17 00:00:00 2001 From: aerych Date: Fri, 29 Sep 2017 19:28:50 -0500 Subject: [PATCH 13/13] Login: Refactor to Swift extensions. Use an enum for supported services. --- .../AccountService+SocialService.swift | 34 ++++++++++++ WordPress/Classes/Services/AccountService.h | 25 --------- WordPress/Classes/Services/AccountService.m | 19 ------- WordPress/WordPress.xcodeproj/project.pbxproj | 18 ++++--- .../WordPressKit.xcodeproj/project.pbxproj | 4 ++ .../WordPressKit/AccountServiceRemote.h | 23 -------- ...countServiceRemoteREST+SocialService.swift | 53 +++++++++++++++++++ .../WordPressKit/AccountServiceRemoteREST.m | 41 -------------- 8 files changed, 102 insertions(+), 115 deletions(-) create mode 100644 WordPress/Classes/Services/AccountService+SocialService.swift create mode 100644 WordPressKit/WordPressKit/AccountServiceRemoteREST+SocialService.swift diff --git a/WordPress/Classes/Services/AccountService+SocialService.swift b/WordPress/Classes/Services/AccountService+SocialService.swift new file mode 100644 index 000000000000..2a17723f28fa --- /dev/null +++ b/WordPress/Classes/Services/AccountService+SocialService.swift @@ -0,0 +1,34 @@ +import Foundation +import WordPressComKit + +extension AccountService { + + /// Connect an account a social service via an ID token. + /// + /// - Parameters: + /// - service The name of the social service. + /// - token The service's OpenID Connect (JWT) ID token for the user. + /// - success + /// - failure + func connectToSocialService(_ service: SocialServiceName, serviceIDToken token:String, success:@escaping (() -> Void), failure:@escaping ((NSError) -> Void)) { + guard let api = defaultWordPressComAccount()?.wordPressComRestApi, + let remote = AccountServiceRemoteREST(wordPressComRestApi: api) else { + fatalError("Failed to initialize a valid remote via the default WordPress.com account.") + } + remote.connectToSocialService(service, serviceIDToken: token, success: success, failure: failure) + } + + /// Disconnect an account a social service via an ID token. + /// - Parameters: + /// - service The name of the social service. + /// - success + /// - failure + func disconnectFromSocialService(_ service: SocialServiceName, success:@escaping (() -> Void), failure:@escaping ((NSError) -> Void)) { + guard let api = defaultWordPressComAccount()?.wordPressComRestApi, + let remote = AccountServiceRemoteREST(wordPressComRestApi: api) else { + fatalError("Failed to initialize a valid remote via the default WordPress.com account.") + } + remote.disconnectFromSocialService(service, success: success, failure: failure) + } + +} diff --git a/WordPress/Classes/Services/AccountService.h b/WordPress/Classes/Services/AccountService.h index 6f452cb6864c..717b139005fc 100644 --- a/WordPress/Classes/Services/AccountService.h +++ b/WordPress/Classes/Services/AccountService.h @@ -93,31 +93,6 @@ extern NSString *const WPAccountEmailAndDefaultBlogUpdatedNotification; - (void)requestAuthenticationLink:(NSString *)email success:(void (^)())success failure:(void (^)(NSError *error))failure; -/** - Connect an account a social service via an ID token. - - @param service The name of the social service. - @param token The service's OpenID Connect (JWT) ID token for the user. - @param success - @param failure - */ -- (void)connectToSocialService:(NSString *)service - serviceIDToken:(NSString *)token - success:(void (^)())success - failure:(void (^)(NSError *error))failure; - - -/** - Disconnect an account a social service via an ID token. - - @param service The name of the social service. - @param success - @param failure - */ -- (void)disconnectFromSocialService:(NSString *)service - success:(void (^)())success - failure:(void (^)(NSError *error))failure; - ///----------------------- /// @name Account creation ///----------------------- diff --git a/WordPress/Classes/Services/AccountService.m b/WordPress/Classes/Services/AccountService.m index 25ac25e63069..292d7b59d696 100644 --- a/WordPress/Classes/Services/AccountService.m +++ b/WordPress/Classes/Services/AccountService.m @@ -197,25 +197,6 @@ - (void)requestAuthenticationLink:(NSString *)email success:(void (^)())success } -- (void)connectToSocialService:(NSString *)service - serviceIDToken:(NSString *)token - success:(void (^)())success - failure:(void (^)(NSError *error))failure -{ - id remote = [self remoteForAccount:[self defaultWordPressComAccount]]; - [remote connectToSocialService:service serviceIDToken:token success:success failure:failure]; -} - - -- (void)disconnectFromSocialService:(NSString *)service - success:(void (^)())success - failure:(void (^)(NSError *error))failure -{ - id remote = [self remoteForAccount:[self defaultWordPressComAccount]]; - [remote disconnectFromSocialService:service success:success failure:failure]; -} - - ///----------------------- /// @name Account creation ///----------------------- diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index eb6a52bb108e..bdbefd0c1125 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -893,6 +893,7 @@ E6C448571CB091AA00458157 /* SigninKeyboardResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6C448561CB091AA00458157 /* SigninKeyboardResponder.swift */; }; E6C892D61C601D55007AD612 /* SharingButtonsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6C892D51C601D55007AD612 /* SharingButtonsViewController.swift */; }; E6D0088E1EF0880C009E5FA3 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6D0088D1EF0880C009E5FA3 /* LoginViewController.swift */; }; + E6D0EE601F7EF9830064D3FC /* AccountService+SocialService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6D0EE5F1F7EF9830064D3FC /* AccountService+SocialService.swift */; }; E6D170371EF9D8D10046D433 /* SiteInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6D170361EF9D8D10046D433 /* SiteInfo.swift */; }; E6D2E15F1B8A9C830000ED14 /* ReaderSiteStreamHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = E6D2E15E1B8A9C830000ED14 /* ReaderSiteStreamHeader.xib */; }; E6D2E1611B8AA4410000ED14 /* ReaderTagStreamHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = E6D2E1601B8AA4410000ED14 /* ReaderTagStreamHeader.xib */; }; @@ -2322,6 +2323,7 @@ E6C448561CB091AA00458157 /* SigninKeyboardResponder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SigninKeyboardResponder.swift; sourceTree = ""; }; E6C892D51C601D55007AD612 /* SharingButtonsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharingButtonsViewController.swift; sourceTree = ""; }; E6D0088D1EF0880C009E5FA3 /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; + E6D0EE5F1F7EF9830064D3FC /* AccountService+SocialService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountService+SocialService.swift"; sourceTree = ""; }; E6D170361EF9D8D10046D433 /* SiteInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiteInfo.swift; sourceTree = ""; }; E6D2E15E1B8A9C830000ED14 /* ReaderSiteStreamHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReaderSiteStreamHeader.xib; sourceTree = ""; }; E6D2E1601B8AA4410000ED14 /* ReaderTagStreamHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReaderTagStreamHeader.xib; sourceTree = ""; }; @@ -2778,7 +2780,7 @@ name = Products; sourceTree = ""; }; - 29B97314FDCFA39411CA2CEA = { + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { isa = PBXGroup; children = ( E1B34C091CCDFFCE00889709 /* Credentials */, @@ -3694,6 +3696,7 @@ B5EFB1C31B31B99D007608A3 /* Facades */, 93C1147D18EC5DD500DAC95C /* AccountService.h */, 93C1147E18EC5DD500DAC95C /* AccountService.m */, + E6D0EE5F1F7EF9830064D3FC /* AccountService+SocialService.swift */, E1FD45DF1C030B3800750F4C /* AccountSettingsService.swift */, 822D60B81F4CCC7A0016C46D /* BlogJetpackSettingsService.swift */, 93C1148318EDF6E100DAC95C /* BlogService.h */, @@ -5485,7 +5488,7 @@ bg, sk, ); - mainGroup = 29B97314FDCFA39411CA2CEA; + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; productRefGroup = 19C28FACFE9D520D11CA2CBB /* Products */; projectDirPath = ""; projectRoot = ""; @@ -5801,7 +5804,7 @@ inputPaths = ( "${SRCROOT}/../Pods/Target Support Files/Pods-WordPress/Pods-WordPress-resources.sh", "${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle", - "$PODS_CONFIGURATION_BUILD_DIR/HockeySDK/HockeySDKResources.bundle", + $PODS_CONFIGURATION_BUILD_DIR/HockeySDK/HockeySDKResources.bundle, ); name = "[CP] Copy Pods Resources"; outputPaths = ( @@ -6170,6 +6173,7 @@ 85D239B61AE5A6170074768D /* ReachabilityFacade.m in Sources */, E6D3B1431D1C702600008D4B /* ReaderFollowedSitesViewController.swift in Sources */, B5899ADE1B419C560075A3D6 /* NotificationSettingDetailsViewController.swift in Sources */, + E6D0EE601F7EF9830064D3FC /* AccountService+SocialService.swift in Sources */, FAE4201A1C5AEFE100C1D036 /* StartOverViewController.swift in Sources */, B57B99D519A2C20200506504 /* NoteTableHeaderView.swift in Sources */, 1790A4531E28F0ED00AE54C2 /* UINavigationController+Helpers.swift in Sources */, @@ -7064,7 +7068,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; - WPCOM_CONFIG = "$HOME/.wpcom_app_credentials"; + WPCOM_CONFIG = $HOME/.wpcom_app_credentials; WPCOM_SCHEME = wpdebug; }; name = Debug; @@ -7130,7 +7134,7 @@ SWIFT_OBJC_BRIDGING_HEADER = "Classes/System/WordPress-Bridging-Header.h"; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; - WPCOM_CONFIG = "$HOME/.wpcom_app_credentials"; + WPCOM_CONFIG = $HOME/.wpcom_app_credentials; WPCOM_SCHEME = wordpress; }; name = Release; @@ -7417,7 +7421,7 @@ SWIFT_OBJC_BRIDGING_HEADER = "Classes/System/WordPress-Bridging-Header.h"; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; - WPCOM_CONFIG = "$HOME/.wpcom_alpha_app_credentials"; + WPCOM_CONFIG = $HOME/.wpcom_alpha_app_credentials; WPCOM_SCHEME = wpalpha; }; name = "Release-Alpha"; @@ -7845,7 +7849,7 @@ SWIFT_OBJC_BRIDGING_HEADER = "Classes/System/WordPress-Bridging-Header.h"; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = "1,2"; - WPCOM_CONFIG = "$HOME/.wpcom_internal_app_credentials"; + WPCOM_CONFIG = $HOME/.wpcom_internal_app_credentials; WPCOM_SCHEME = wpinternal; }; name = "Release-Internal"; diff --git a/WordPressKit/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit/WordPressKit.xcodeproj/project.pbxproj index 77aa73c4ebec..5bf0a8d4aba8 100644 --- a/WordPressKit/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit/WordPressKit.xcodeproj/project.pbxproj @@ -339,6 +339,7 @@ E632D7781F6E047400297F6D /* SocialLogin2FANonceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E632D7771F6E047400297F6D /* SocialLogin2FANonceInfo.swift */; }; E6C1E8491EF21FC100D139D9 /* is-passwordless-account-no-account-found.json in Resources */ = {isa = PBXBuildFile; fileRef = E6C1E8471EF21FC100D139D9 /* is-passwordless-account-no-account-found.json */; }; E6C1E84A1EF21FC100D139D9 /* is-passwordless-account-success.json in Resources */ = {isa = PBXBuildFile; fileRef = E6C1E8481EF21FC100D139D9 /* is-passwordless-account-success.json */; }; + E6D0EE621F7EF9CE0064D3FC /* AccountServiceRemoteREST+SocialService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6D0EE611F7EF9CE0064D3FC /* AccountServiceRemoteREST+SocialService.swift */; }; F8B82B9E92C9637430D7D032 /* Pods_WordPressKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F902B70CBB63DD7EB9D275A /* Pods_WordPressKitTests.framework */; }; /* End PBXBuildFile section */ @@ -697,6 +698,7 @@ E632D7771F6E047400297F6D /* SocialLogin2FANonceInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocialLogin2FANonceInfo.swift; sourceTree = ""; }; E6C1E8471EF21FC100D139D9 /* is-passwordless-account-no-account-found.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "is-passwordless-account-no-account-found.json"; sourceTree = ""; }; E6C1E8481EF21FC100D139D9 /* is-passwordless-account-success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "is-passwordless-account-success.json"; sourceTree = ""; }; + E6D0EE611F7EF9CE0064D3FC /* AccountServiceRemoteREST+SocialService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountServiceRemoteREST+SocialService.swift"; sourceTree = ""; }; EF1D8EBEFA46779A06F52788 /* Pods-WordPressKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKitTests.release.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressKitTests/Pods-WordPressKitTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -972,6 +974,7 @@ 93BD27381EE73282002BB00B /* AccountServiceRemote.h */, 93BD27391EE73282002BB00B /* AccountServiceRemoteREST.h */, 93BD273A1EE73282002BB00B /* AccountServiceRemoteREST.m */, + E6D0EE611F7EF9CE0064D3FC /* AccountServiceRemoteREST+SocialService.swift */, 7403A2E31EF06ED500DED7DC /* AccountSettingsRemote.swift */, 82FFBF551F460DD400F4573F /* BlogJetpackSettingsServiceRemote.swift */, 74B5F0DB1EF829B800B411E7 /* BlogServiceRemote.h */, @@ -1770,6 +1773,7 @@ 93BD276A1EE736A8002BB00B /* RemoteUser.m in Sources */, 742362D71F10250600BD0A7F /* MenusServiceRemote.m in Sources */, 9368C7BA1EC630270092CE8E /* StatsSummary.m in Sources */, + E6D0EE621F7EF9CE0064D3FC /* AccountServiceRemoteREST+SocialService.swift in Sources */, 7430C9B61F1927C50051B8E6 /* RemoteReaderSiteInfo.m in Sources */, 74DA56351F06EAF000FE9BF4 /* MediaServiceRemoteXMLRPC.m in Sources */, 93F50A381F226B9300B5BEBA /* WordPressComServiceRemote.m in Sources */, diff --git a/WordPressKit/WordPressKit/AccountServiceRemote.h b/WordPressKit/WordPressKit/AccountServiceRemote.h index 2c091d58978a..1372b23d07c2 100644 --- a/WordPressKit/WordPressKit/AccountServiceRemote.h +++ b/WordPressKit/WordPressKit/AccountServiceRemote.h @@ -82,27 +82,4 @@ success:(void (^)())success failure:(void (^)(NSError *error))failure; -/** - * @brief Connect to the specified social service via its OpenID Connect (JWT) token. - * - * @param service The name of the social service. - * @param token The OpenID Connect (JWT) ID token identifying the user on the social service. - * @param success The block that will be executed on success. Can be nil. - * @param failure The block that will be executed on failure. Can be nil. - */ -- (void)connectToSocialService:(NSString *)service - serviceIDToken:(NSString *)token - success:(void (^)())success - failure:(void (^)(NSError *error))failure; - -/** - * @brief Disconnect fromm the specified social service. - * - * @param service The name of the social service. - * @param success The block that will be executed on success. Can be nil. - * @param failure The block that will be executed on failure. Can be nil. - */ -- (void)disconnectFromSocialService:(NSString *)service - success:(void (^)())success - failure:(void (^)(NSError *error))failure; @end diff --git a/WordPressKit/WordPressKit/AccountServiceRemoteREST+SocialService.swift b/WordPressKit/WordPressKit/AccountServiceRemoteREST+SocialService.swift new file mode 100644 index 000000000000..3a21b4a7d04d --- /dev/null +++ b/WordPressKit/WordPressKit/AccountServiceRemoteREST+SocialService.swift @@ -0,0 +1,53 @@ +import Foundation + +public enum SocialServiceName: String { + case google +} + + +extension AccountServiceRemoteREST { + + /// Connect to the specified social service via its OpenID Connect (JWT) token. + /// + /// - Parameters: + /// - service The name of the social service. + /// - token The OpenID Connect (JWT) ID token identifying the user on the social service. + /// - success The block that will be executed on success. + /// - failure The block that will be executed on failure. + public func connectToSocialService(_ service: SocialServiceName, serviceIDToken token: String, success:@escaping (() -> Void), failure:@escaping ((NSError) -> Void)) { + guard let path = self.path(forEndpoint: "me/social-login/connect", with: .version_1_1) else { + // This should never fail but if it does we don't want to ignore the problem. + fatalError("There was a problem creating a valid path for the supplied endpoint and REST API version.") + } + let params = [ + "service": service.rawValue, + "id_token": token + ] as [String: AnyObject] + wordPressComRestApi.POST(path, parameters: params, success: { (responseObject, httpResponse) in + success() + }, failure: { (error, httpResponse) in + failure(error) + }) + } + + /// Disconnect fromm the specified social service. + /// + /// - Parameters: + /// - service The name of the social service. + /// - success The block that will be executed on success. + /// - failure The block that will be executed on failure. + public func disconnectFromSocialService(_ service: SocialServiceName, success:@escaping(() -> Void), failure:@escaping((NSError) -> Void)) { + guard let path = self.path(forEndpoint: "me/social-login/disconnect", with: .version_1_1) else { + // This should never fail but if it does we don't want to ignore the problem. + fatalError("There was a problem creating a valid path for the supplied endpoint and REST API version.") + } + let params = [ + "service": service.rawValue, + ] as [String: AnyObject] + wordPressComRestApi.POST(path, parameters: params, success: { (responseObject, httpResponse) in + success() + }, failure: { (error, httpResponse) in + failure(error) + }) + } +} diff --git a/WordPressKit/WordPressKit/AccountServiceRemoteREST.m b/WordPressKit/WordPressKit/AccountServiceRemoteREST.m index 6dd6ea3a36ec..146a473bb47c 100644 --- a/WordPressKit/WordPressKit/AccountServiceRemoteREST.m +++ b/WordPressKit/WordPressKit/AccountServiceRemoteREST.m @@ -233,47 +233,6 @@ - (void)requestWPComAuthLinkForEmail:(NSString *)email }]; } -- (void)connectToSocialService:(NSString *)service - serviceIDToken:(NSString *)token - success:(void (^)())success - failure:(void (^)(NSError *error))failure -{ - NSString *path = [self pathForEndpoint:@"me/social-login/connect" withVersion:ServiceRemoteWordPressComRESTApiVersion_1_1]; - NSDictionary *params = @{ - @"service":service, - @"id_token":token - }; - [self.wordPressComRestApi POST:path parameters:params success:^(id responseObject, NSHTTPURLResponse *httpResponse) { - if (success) { - success(); - } - } failure:^(NSError *error, NSHTTPURLResponse *httpResponse) { - if (failure) { - failure(error); - } - }]; -} - -- (void)disconnectFromSocialService:(NSString *)service - success:(void (^)())success - failure:(void (^)(NSError *error))failure -{ - NSString *path = [self pathForEndpoint:@"me/social-login/disconnect" withVersion:ServiceRemoteWordPressComRESTApiVersion_1_1]; - NSDictionary *params = @{ - @"service":service - }; - [self.wordPressComRestApi POST:path parameters:params success:^(id responseObject, NSHTTPURLResponse *httpResponse) { - if (success) { - success(); - } - } failure:^(NSError *error, NSHTTPURLResponse *httpResponse) { - if (failure) { - failure(error); - } - }]; -} - - #pragma mark - Private Methods - (RemoteUser *)remoteUserFromDictionary:(NSDictionary *)dictionary