From 6fb495387c07a9b10d663b0b4f2edeba753a0cb9 Mon Sep 17 00:00:00 2001 From: aerych Date: Sun, 8 Oct 2017 15:32:46 -0500 Subject: [PATCH 1/8] Refactors error handling. Keep response obj via userInfo for failure blocks. --- .../WordPressComOAuthClient.swift | 98 ++++++++++++++----- 1 file changed, 74 insertions(+), 24 deletions(-) diff --git a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift index 65a31bc7224e..b80bbe9fd1bb 100644 --- a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift +++ b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift @@ -8,6 +8,7 @@ import CocoaLumberjack case invalidRequest case needsMultifactorCode case invalidOneTimePassword + case socialLoginExistingUserUnconnected } /// `WordPressComOAuthClient` encapsulates the pattern of authenticating against WordPress.com OAuth2 service. @@ -17,6 +18,7 @@ import CocoaLumberjack /// public final class WordPressComOAuthClient: NSObject { + public static let WordPressComOAuthErrorResponseObjectKey = "WordPressComOAuthErrorResponseObjectKey" 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&version=1.0" @@ -308,37 +310,85 @@ final class WordPressComOAuthResponseSerializer: AFJSONResponseSerializer { super.init(coder: aDecoder) } + + /// Possible 400 errors: + /// - invalid_client: client_id is missing or wrong, it shouldn't happen + /// - unsupported_grant_type: client_id doesn't support password grants + /// - invalid_request: A required field is missing/malformed + /// - invalid_request: Authentication failed + /// - needs_2fa: Multifactor Authentication code is required + /// - user_exists: Returned by the social login endpoint if a wpcom user is found, but not connected to a social service. + /// + let errorsMap = [ + "invalid_client": WordPressComOAuthError.invalidClient, + "unsupported_grant_type": WordPressComOAuthError.unsupportedGrantType, + "invalid_request": WordPressComOAuthError.invalidRequest, + "needs_2fa": WordPressComOAuthError.needsMultifactorCode, + "invalid_otp": WordPressComOAuthError.invalidOneTimePassword, + "user_exists": WordPressComOAuthError.socialLoginExistingUserUnconnected, + ] + + + /// Overridden to provide custom error handling. Some HTTP requests include + /// a response body even in a failure scenario. Since AFNetworking does not + /// pass a responseObject (if any) to a failure block this method ensures + /// it is available via an error's userInfo dictionary. + /// + /// - Parameters: + /// - response: The URL response. + /// - data: Data returned from the request. + /// - error: A pointer to an error (if any). + /// - Returns: The response object or nil. override func responseObject(for response: URLResponse?, data: Data?, error: NSErrorPointer) -> Any? { let responseObject = super.responseObject(for: response, data: data, error: error) - guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 400, - let responseDictionary = responseObject as? [String: AnyObject], - let errorCode = responseDictionary["error"] as? String, - let errorDescription = responseDictionary["error_description"] as? String - else { - return responseObject as AnyObject? + guard let httpResponse = response as? HTTPURLResponse else { + return responseObject } - /// Possible errors: - /// - invalid_client: client_id is missing or wrong, it shouldn't happen - /// - unsupported_grant_type: client_id doesn't support password grants - /// - invalid_request: A required field is missing/malformed - /// - invalid_request: Authentication failed - /// - needs_2fa: Multifactor Authentication code is required - /// - let errorsMap = [ - "invalid_client": WordPressComOAuthError.invalidClient, - "unsupported_grant_type": WordPressComOAuthError.unsupportedGrantType, - "invalid_request": WordPressComOAuthError.invalidRequest, - "needs_2fa": WordPressComOAuthError.needsMultifactorCode, - "invalid_otp" : WordPressComOAuthError.invalidOneTimePassword - ] + // Handle known 400 errors. + if httpResponse.statusCode == 400 { + // REST API Error format + if let responseDictionary = responseObject as? [String: AnyObject], + let errorCode = responseDictionary["error"] as? String, + let errorDescription = responseDictionary["error_description"] as? String { - let mappedCode = errorsMap[errorCode]?.rawValue ?? WordPressComOAuthError.unknown.rawValue + error?.pointee = errorFor(errorCode: errorCode, errorDescription: errorDescription, responseObject: responseObject) + } + + } else if httpResponse.statusCode == 409 { + // Social login user-exists error + if let responseDict = responseObject as? [String: AnyObject], + let data = responseDict["data"] as? [String: AnyObject], + let errors = data["errors"] as? NSArray, + let err = errors[0] as? [String: AnyObject], + let errorCode = err["code"] as? String, + let errorDescription = err["message"] as? String { + + error?.pointee = errorFor(errorCode: errorCode, errorDescription: errorDescription, responseObject: responseObject) + } + } - error?.pointee = NSError(domain: WordPressComOAuthClient.WordPressComOAuthErrorDomain, - code: mappedCode, - userInfo: [NSLocalizedDescriptionKey: errorDescription]) return responseObject as AnyObject? } + + + /// Creates an NSError from the supplied arguements. The response object is + /// added to the error's userInfo dictionary. + /// + /// - Parameters: + /// - errorCode: A string representing the error code. This is not the same as an HTTP status code. + /// - errorDescription: A description of the error. + /// - responseObject: The responseObject (if any) that was passed with the error. + /// - Returns: An NSError. + func errorFor(errorCode: String, errorDescription: String, responseObject: Any?) -> NSError { + var userInfo:[String: AnyObject] = [NSLocalizedDescriptionKey: errorDescription as AnyObject] + if let responseObject = responseObject { + userInfo[WordPressComOAuthClient.WordPressComOAuthErrorResponseObjectKey] = responseObject as AnyObject + } + let mappedCode = errorsMap[errorCode]?.rawValue ?? WordPressComOAuthError.unknown.rawValue + return NSError(domain: WordPressComOAuthClient.WordPressComOAuthErrorDomain, + code: mappedCode, + userInfo: userInfo) + } } From 0e120f15008b7f3966b64457646492210c7736f1 Mon Sep 17 00:00:00 2001 From: aerych Date: Sun, 8 Oct 2017 16:29:06 -0500 Subject: [PATCH 2/8] Login: Handle a user_exists error. Show the password screen. --- WordPress/Classes/Services/LoginFacade.h | 15 ++++++++++++++- WordPress/Classes/Services/LoginFacade.m | 4 ++++ .../Services/WordPressComOAuthClientFacade.h | 1 + .../Services/WordPressComOAuthClientFacade.m | 3 ++- .../NUX/LoginEmailViewController.swift | 8 ++++++++ .../WordPressKit/WordPressComOAuthClient.swift | 18 +++++++++++++++++- 6 files changed, 46 insertions(+), 3 deletions(-) diff --git a/WordPress/Classes/Services/LoginFacade.h b/WordPress/Classes/Services/LoginFacade.h index 61d0ec6bb3d3..6e52a79190ec 100644 --- a/WordPress/Classes/Services/LoginFacade.h +++ b/WordPress/Classes/Services/LoginFacade.h @@ -44,7 +44,11 @@ */ - (void)requestOneTimeCodeWithLoginFields:(LoginFields *)loginFields; - +/** + * Social login via google. + * + * @param googleIDToken A Google id_token. + */ - (void)loginToWordPressDotComWithGoogleIDToken:(NSString *)googleIDToken; /** @@ -134,5 +138,14 @@ */ - (void)finishedLoginWithGoogleIDToken:(NSString *)googleIDToken authToken:(NSString *)authToken; + +/** + * Lets the delegate know that a social login attempt found a matching user, but + * their account has not been connected to the social service previously. + * + * @param email The email address that was matched. + */ +- (void)existingUserNeedsConnection:(NSString *)email; + @end diff --git a/WordPress/Classes/Services/LoginFacade.m b/WordPress/Classes/Services/LoginFacade.m index 3c20c3c87c0b..7cbb9ef88e88 100644 --- a/WordPress/Classes/Services/LoginFacade.m +++ b/WordPress/Classes/Services/LoginFacade.m @@ -75,6 +75,10 @@ - (void)loginToWordPressDotComWithGoogleIDToken:(NSString *)googleIDToken if ([self.delegate respondsToSelector:@selector(needsMultifactorCodeForUserID:andNonceInfo:)]) { [self.delegate needsMultifactorCodeForUserID:userID andNonceInfo:nonceInfo]; } + } existingUserNeedsConnection: ^(NSString *email) { + if ([self.delegate respondsToSelector:@selector(existingUserNeedsConnection:)]) { + [self.delegate existingUserNeedsConnection: email]; + } } 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 f8565cd9a2aa..ab69c771f922 100644 --- a/WordPress/Classes/Services/WordPressComOAuthClientFacade.h +++ b/WordPress/Classes/Services/WordPressComOAuthClientFacade.h @@ -19,6 +19,7 @@ - (void)authenticateWithGoogleIDToken:(NSString *)token success:(void (^)(NSString *authToken))success needsMultiFactor:(void (^)(NSInteger userID, SocialLogin2FANonceInfo *nonceInfo))needsMultifactor + existingUserNeedsConnection:(void (^)(NSString *email))existingUserNeedsConnection failure:(void (^)(NSError *error))failure; - (void)authenticateSocialLoginUser:(NSInteger)userID diff --git a/WordPress/Classes/Services/WordPressComOAuthClientFacade.m b/WordPress/Classes/Services/WordPressComOAuthClientFacade.m index 7e61e810999c..1d56fe46d5f4 100644 --- a/WordPress/Classes/Services/WordPressComOAuthClientFacade.m +++ b/WordPress/Classes/Services/WordPressComOAuthClientFacade.m @@ -36,10 +36,11 @@ - (void)requestOneTimeCodeWithUsername:(NSString *)username - (void)authenticateWithGoogleIDToken:(NSString *)token success:(void (^)(NSString *authToken))success needsMultiFactor:(void (^)(NSInteger userID, SocialLogin2FANonceInfo *nonceInfo))needsMultifactor + existingUserNeedsConnection:(void (^)(NSString *email))existingUserNeedsConnection failure:(void (^)(NSError *error))failure { WordPressComOAuthClient *client = [WordPressComOAuthClient clientWithClientID:ApiCredentials.client secret:ApiCredentials.secret]; - [client authenticateWithIDToken:token success:success needsMultifactor:needsMultifactor failure:failure]; + [client authenticateWithIDToken:token success:success needsMultifactor:needsMultifactor existingUserNeedsConnection:existingUserNeedsConnection failure:failure]; } - (void)authenticateSocialLoginUser:(NSInteger)userID diff --git a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift index 4eb783c67a84..adcf3d83d709 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift @@ -387,6 +387,14 @@ extension LoginEmailViewController { GIDSignIn.sharedInstance().disconnect() } + + func existingUserNeedsConnection(_ email: String!) { + loginFields.username = email + + performSegue(withIdentifier: NUXAbstractViewController.SegueIdentifier.showWPComLogin, sender: self) + } + + func needsMultifactorCode(forUserID userID: Int, andNonceInfo nonceInfo: SocialLogin2FANonceInfo!) { // TODO: to be implemented. } diff --git a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift index b80bbe9fd1bb..fa4c5ea546f9 100644 --- a/WordPressKit/WordPressKit/WordPressComOAuthClient.swift +++ b/WordPressKit/WordPressKit/WordPressComOAuthClient.swift @@ -151,6 +151,7 @@ public final class WordPressComOAuthClient: NSObject { public func authenticateWithIDToken(_ token: String, success: @escaping (_ authToken: String?) -> Void, needsMultifactor: @escaping (_ userID: Int, _ nonceInfo: SocialLogin2FANonceInfo) -> Void, + existingUserNeedsConnection: @escaping (_ email: String) -> Void, failure: @escaping (_ error: NSError) -> Void ) { let parameters = [ "client_id": clientID, @@ -192,7 +193,22 @@ public final class WordPressComOAuthClient: NSObject { needsMultifactor(userID, nonceInfo) }, failure: { (task, error) in - failure(error as NSError) + let err = error as NSError + + // Inspect the error and handle the case of an existing user. + if err.code == WordPressComOAuthError.socialLoginExistingUserUnconnected.rawValue && + err.domain == WordPressComOAuthClient.WordPressComOAuthErrorDomain { + // Get the responseObject from the userInfo dict. + // Extract the email address for the callback. + if let responseDict = err.userInfo[WordPressComOAuthClient.WordPressComOAuthErrorResponseObjectKey] as? [String: AnyObject], + let data = responseDict["data"] as? [String: AnyObject], + let email = data["email"] as? String { + + existingUserNeedsConnection(email) + return + } + } + failure(err) } ) } From efb908d53fbf05512bb0e18507305f3be08c9b7d Mon Sep 17 00:00:00 2001 From: aerych Date: Sun, 8 Oct 2017 16:39:52 -0500 Subject: [PATCH 3/8] Login: Save email. Disconnect Google. Flag auth as social log in. --- .../Classes/ViewRelated/NUX/LoginEmailViewController.swift | 7 +++++++ WordPress/Classes/ViewRelated/NUX/LoginFields.swift | 3 +++ .../Classes/ViewRelated/NUX/LoginViewController.swift | 4 +++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift index adcf3d83d709..01c92b713159 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift @@ -147,6 +147,9 @@ class LoginEmailViewController: LoginViewController, SigninKeyboardResponder { // For paranoia, make sure a Google account is not already signed in / cached. GIDSignIn.sharedInstance().disconnect() + // Flag this as a social sign in. + loginFields.meta.socialService = SocialServiceName.google.rawValue + // Configure all the things and sign in. GIDSignIn.sharedInstance().delegate = self GIDSignIn.sharedInstance().uiDelegate = self @@ -389,7 +392,11 @@ extension LoginEmailViewController { func existingUserNeedsConnection(_ email: String!) { + // Disconnect now that we're done with Google. + GIDSignIn.sharedInstance().disconnect() + loginFields.username = email + loginFields.emailAddress = email performSegue(withIdentifier: NUXAbstractViewController.SegueIdentifier.showWPComLogin, sender: self) } diff --git a/WordPress/Classes/ViewRelated/NUX/LoginFields.swift b/WordPress/Classes/ViewRelated/NUX/LoginFields.swift index a25434ec9774..b09a1627eae1 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginFields.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginFields.swift @@ -79,4 +79,7 @@ class LoginFieldsMeta: NSObject { /// Flags whether a 2fa challenge had to be satisfied before a log in could be complete. /// Included in analytics after a successful login. var requiredMultifactor = false // A 2fa prompt was needed. + + /// Identifies a social login and the service used. + var socialService = "" } diff --git a/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift b/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift index 28958d1c59cc..b8a331668807 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift @@ -103,11 +103,13 @@ class LoginViewController: NUXAbstractViewController { } /// Validates what is entered in the various form fields and, if valid, - /// proceeds with login. + /// proceeds with login. Empties loginFields.meta.socialService as + /// social signin does not require form validation. /// func validateFormAndLogin() { view.endEditing(true) displayError(message: "") + loginFields.meta.socialService = "" // Is everything filled out? if !SigninHelpers.validateFieldsPopulatedForSignin(loginFields) { From 56044e71d6a563ea2430529ee2f4ef875815e188 Mon Sep 17 00:00:00 2001 From: aerych Date: Sun, 8 Oct 2017 16:52:38 -0500 Subject: [PATCH 4/8] Login: Use enum for meta.socialService. Show correct instruction text on password screen. --- .../Classes/ViewRelated/NUX/LoginEmailViewController.swift | 2 +- WordPress/Classes/ViewRelated/NUX/LoginFields.swift | 2 +- WordPress/Classes/ViewRelated/NUX/LoginViewController.swift | 2 +- .../Classes/ViewRelated/NUX/LoginWPComViewController.swift | 6 ++++++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift index 01c92b713159..dff1d1701966 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift @@ -148,7 +148,7 @@ class LoginEmailViewController: LoginViewController, SigninKeyboardResponder { GIDSignIn.sharedInstance().disconnect() // Flag this as a social sign in. - loginFields.meta.socialService = SocialServiceName.google.rawValue + loginFields.meta.socialService = SocialServiceName.google // Configure all the things and sign in. GIDSignIn.sharedInstance().delegate = self diff --git a/WordPress/Classes/ViewRelated/NUX/LoginFields.swift b/WordPress/Classes/ViewRelated/NUX/LoginFields.swift index b09a1627eae1..4918dfb23c78 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginFields.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginFields.swift @@ -81,5 +81,5 @@ class LoginFieldsMeta: NSObject { var requiredMultifactor = false // A 2fa prompt was needed. /// Identifies a social login and the service used. - var socialService = "" + var socialService: SocialServiceName? } diff --git a/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift b/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift index b8a331668807..822582c91328 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift @@ -109,7 +109,7 @@ class LoginViewController: NUXAbstractViewController { func validateFormAndLogin() { view.endEditing(true) displayError(message: "") - loginFields.meta.socialService = "" + loginFields.meta.socialService = nil // Is everything filled out? if !SigninHelpers.validateFieldsPopulatedForSignin(loginFields) { diff --git a/WordPress/Classes/ViewRelated/NUX/LoginWPComViewController.swift b/WordPress/Classes/ViewRelated/NUX/LoginWPComViewController.swift index a14848acac6a..7a02014e395d 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginWPComViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginWPComViewController.swift @@ -120,6 +120,12 @@ class LoginWPComViewController: LoginViewController, SigninKeyboardResponder { } func localizeControls() { + if let service = loginFields.meta.socialService, service == SocialServiceName.google { + instructionLabel?.text = NSLocalizedString("To proceed with this Google account, please first log in with your WordPress.com password. This will only be asked once.", comment: "") + } else { + instructionLabel?.text = NSLocalizedString("Enter the password for your WordPress.com account.", comment: "Instructional text shown when requesting the user's password for login.") + } + passwordField?.placeholder = NSLocalizedString("Password", comment: "Password placeholder") passwordField?.accessibilityIdentifier = "Password" From 791223b6855ec8e45b84db7e6d23e218b764ae56 Mon Sep 17 00:00:00 2001 From: aerych Date: Sun, 8 Oct 2017 17:50:49 -0500 Subject: [PATCH 5/8] Login: Pass client id and secret when making the call to connect. --- .../Services/AccountService+SocialService.swift | 2 +- .../AccountServiceRemoteREST+SocialService.swift | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/WordPress/Classes/Services/AccountService+SocialService.swift b/WordPress/Classes/Services/AccountService+SocialService.swift index f29d41e9c18b..4597e2cfd299 100644 --- a/WordPress/Classes/Services/AccountService+SocialService.swift +++ b/WordPress/Classes/Services/AccountService+SocialService.swift @@ -15,7 +15,7 @@ extension AccountService { 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) + remote.connectToSocialService(service, serviceIDToken: token, oAuthClientID: ApiCredentials.client(), oAuthClientSecret: ApiCredentials.secret(), success: success, failure: failure) } /// Disconnect an account a social service via an ID token. diff --git a/WordPressKit/WordPressKit/AccountServiceRemoteREST+SocialService.swift b/WordPressKit/WordPressKit/AccountServiceRemoteREST+SocialService.swift index 3a21b4a7d04d..af5f01f74440 100644 --- a/WordPressKit/WordPressKit/AccountServiceRemoteREST+SocialService.swift +++ b/WordPressKit/WordPressKit/AccountServiceRemoteREST+SocialService.swift @@ -12,16 +12,20 @@ extension AccountServiceRemoteREST { /// - Parameters: /// - service The name of the social service. /// - token The OpenID Connect (JWT) ID token identifying the user on the social service. + /// - oAuthClientID The WPCOM REST API client ID. + /// - oAuthClientSecret The WPCOM REST API client secret. /// - 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)) { + public func connectToSocialService(_ service: SocialServiceName, serviceIDToken token: String, oAuthClientID: String, oAuthClientSecret: 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 = [ + "client_id": oAuthClientID, + "client_secret": oAuthClientSecret, "service": service.rawValue, - "id_token": token + "id_token": token, ] as [String: AnyObject] wordPressComRestApi.POST(path, parameters: params, success: { (responseObject, httpResponse) in success() @@ -34,14 +38,18 @@ extension AccountServiceRemoteREST { /// /// - Parameters: /// - service The name of the social service. + /// - oAuthClientID The WPCOM REST API client ID. + /// - oAuthClientSecret The WPCOM REST API client secret. /// - 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)) { + public func disconnectFromSocialService(_ service: SocialServiceName, oAuthClientID: String, oAuthClientSecret: String, 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 = [ + "client_id": oAuthClientID, + "client_secret": oAuthClientSecret, "service": service.rawValue, ] as [String: AnyObject] wordPressComRestApi.POST(path, parameters: params, success: { (responseObject, httpResponse) in From 663f55dd3188c28edd95cb32688879e497a58975 Mon Sep 17 00:00:00 2001 From: aerych Date: Sun, 8 Oct 2017 17:51:53 -0500 Subject: [PATCH 6/8] Login: Store the id_token in loginFields meta. --- WordPress/Classes/ViewRelated/NUX/LoginFields.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WordPress/Classes/ViewRelated/NUX/LoginFields.swift b/WordPress/Classes/ViewRelated/NUX/LoginFields.swift index 4918dfb23c78..84fa9eb98f14 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginFields.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginFields.swift @@ -82,4 +82,6 @@ class LoginFieldsMeta: NSObject { /// Identifies a social login and the service used. var socialService: SocialServiceName? + + var socialServiceIDToken: String? } From a87c0657db872c40fcfe9cca050743179cdbe95b Mon Sep 17 00:00:00 2001 From: aerych Date: Sun, 8 Oct 2017 17:52:26 -0500 Subject: [PATCH 7/8] Login: Connect a wpcom account to the google account. --- .../NUX/LoginEmailViewController.swift | 7 +++++-- .../ViewRelated/NUX/LoginViewController.swift | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift index dff1d1701966..55d93a379f52 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginEmailViewController.swift @@ -273,9 +273,11 @@ class LoginEmailViewController: LoginViewController, SigninKeyboardResponder { /// Validates what is entered in the various form fields and, if valid, - /// proceeds with the submit action. + /// proceeds with the submit action. Empties loginFields.meta.socialService as + /// social signin does not require form validation. /// func validateForm() { + loginFields.meta.socialService = nil displayError(message: "") guard EmailFormatValidator.validate(string: loginFields.username) else { assertionFailure("Form should not be submitted unless there is a valid looking email entered.") @@ -417,9 +419,10 @@ extension LoginEmailViewController: GIDSignInDelegate { return } - // Store the email address. + // Store the email address and token. loginFields.emailAddress = email loginFields.username = email + loginFields.meta.socialServiceIDToken = token configureViewLoading(true) diff --git a/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift b/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift index 822582c91328..1677044eecd1 100644 --- a/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift +++ b/WordPress/Classes/ViewRelated/NUX/LoginViewController.swift @@ -103,13 +103,11 @@ class LoginViewController: NUXAbstractViewController { } /// Validates what is entered in the various form fields and, if valid, - /// proceeds with login. Empties loginFields.meta.socialService as - /// social signin does not require form validation. + /// proceeds with login. /// func validateFormAndLogin() { view.endEditing(true) displayError(message: "") - loginFields.meta.socialService = nil // Is everything filled out? if !SigninHelpers.validateFieldsPopulatedForSignin(loginFields) { @@ -158,6 +156,17 @@ extension LoginViewController: SigninWPComSyncHandler, LoginFacadeDelegate { func finishedLogin(withUsername username: String!, authToken: String!, requiredMultifactorCode: Bool) { syncWPCom(username, authToken: authToken, requiredMultifactor: requiredMultifactorCode) + guard let service = loginFields.meta.socialService, service == SocialServiceName.google, + let token = loginFields.meta.socialServiceIDToken else { + return + } + + let accountService = AccountService(managedObjectContext: ContextManager.sharedInstance().mainContext) + accountService.connectToSocialService(service, serviceIDToken: token, success: { + // noop + }, failure: { error in + DDLogError(error.description) + }) } func displayRemoteError(_ error: Error!) { From f4857a853d128ffd3f551d48e127b6feec56b73b Mon Sep 17 00:00:00 2001 From: aerych Date: Fri, 13 Oct 2017 06:43:39 -0400 Subject: [PATCH 8/8] Login: Fixes method signature after change. --- WordPress/Classes/Services/AccountService+SocialService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/Classes/Services/AccountService+SocialService.swift b/WordPress/Classes/Services/AccountService+SocialService.swift index 4597e2cfd299..ca1fd8eeb5b4 100644 --- a/WordPress/Classes/Services/AccountService+SocialService.swift +++ b/WordPress/Classes/Services/AccountService+SocialService.swift @@ -28,7 +28,7 @@ extension AccountService { 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) + remote.disconnectFromSocialService(service, oAuthClientID: ApiCredentials.client(), oAuthClientSecret: ApiCredentials.secret(), success: success, failure: failure) } }