Skip to content
This repository was archived by the owner on Feb 5, 2025. It is now read-only.

Commit db73c6a

Browse files
Merge pull request #553 from wordpress-mobile/try/fix-magic-link-login-and-signup
Fix magic link issues.
2 parents f3ec739 + de314ed commit db73c6a

File tree

13 files changed

+142
-199
lines changed

13 files changed

+142
-199
lines changed

Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ PODS:
4848
- Specta (1.0.7)
4949
- SVProgressHUD (2.2.5)
5050
- UIDeviceIdentifier (1.6.0)
51-
- WordPressKit (4.24.0-beta.4):
51+
- WordPressKit (4.24.0-beta.6):
5252
- Alamofire (~> 4.8.0)
5353
- CocoaLumberjack (~> 3.4)
5454
- NSObject-SafeExpectations (= 0.0.4)
@@ -123,7 +123,7 @@ SPEC CHECKSUMS:
123123
Specta: 3e1bd89c3517421982dc4d1c992503e48bd5fe66
124124
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
125125
UIDeviceIdentifier: f4bf3b343581a1beacdbf5fb1a8825bd5f05a4a4
126-
WordPressKit: d084acd74bafd78d70ccb95686ceb927c825324e
126+
WordPressKit: 6fde9c226f9570e7c440c315a93b58a507dc8db1
127127
WordPressShared: 38cb62e9cb998d4dc3c1611f17934c6875a6b3e8
128128
WordPressUI: 1cf47a3b78154faf69caa18569ee7ece1e510fa0
129129
wpxmlrpc: bf55a43a7e710bd2a4fb8c02dfe83b1246f14f13

WordPressAuthenticator.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "WordPressAuthenticator"
3-
s.version = "1.33.0-beta.3"
3+
s.version = "1.33.0-beta.4"
44
s.summary = "WordPressAuthenticator implements an easy and elegant way to authenticate your WordPress Apps."
55

66
s.description = <<-DESC

WordPressAuthenticator/Authenticator/WordPressAuthenticator.swift

Lines changed: 80 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import UIKit
1+
import AuthenticationServices
22
import CocoaLumberjack
3-
import NSURL_IDN
43
import GoogleSignIn
4+
import NSURL_IDN
5+
import UIKit
56
import WordPressShared
67
import WordPressUI
7-
import AuthenticationServices
8+
import WordPressKit
89

910
// MARK: - WordPressAuthenticator: Public API to deal with WordPress.com and WordPress.org authentication.
1011
//
@@ -56,14 +57,9 @@ import AuthenticationServices
5657
///
5758
@objc public static let WPSigninDidFinishNotification = "WPSigninDidFinishNotification"
5859

59-
/// Internal Constants.
60+
/// The host name that identifies magic link URLs
6061
///
61-
private enum Constants {
62-
static let authenticationInfoKey = "authenticationInfoKey"
63-
static let username = "username"
64-
static let emailMagicLinkSource = "emailMagicLinkSource"
65-
static let magicLinkUrlPath = "magic-login"
66-
}
62+
private static let magicLinkUrlHostname = "magic-login"
6763

6864
// MARK: - Initialization
6965

@@ -126,7 +122,7 @@ import AuthenticationServices
126122
/// Indicates if the received URL is a WordPress.com Authentication Callback.
127123
///
128124
@objc public func isWordPressAuthUrl(_ url: URL) -> Bool {
129-
let expectedPrefix = configuration.wpcomScheme + "://" + Constants.magicLinkUrlPath
125+
let expectedPrefix = configuration.wpcomScheme + "://" + Self.magicLinkUrlHostname
130126
return url.absoluteString.hasPrefix(expectedPrefix)
131127
}
132128

@@ -138,8 +134,8 @@ import AuthenticationServices
138134

139135
/// Attempts to process the specified URL as a WordPress Authentication Link. Returns *true* on success.
140136
///
141-
@objc public func handleWordPressAuthUrl(_ url: URL, rootViewController: UIViewController) -> Bool {
142-
return WordPressAuthenticator.openAuthenticationURL(url, fromRootViewController: rootViewController)
137+
@objc public func handleWordPressAuthUrl(_ url: URL, rootViewController: UIViewController, automatedTesting: Bool = false) -> Bool {
138+
return WordPressAuthenticator.openAuthenticationURL(url, fromRootViewController: rootViewController, automatedTesting: automatedTesting)
143139
}
144140

145141

@@ -309,68 +305,91 @@ import AuthenticationServices
309305

310306
// MARK: - Authentication Link Helpers
311307

312-
313308
/// Present a signin view controller to handle an authentication link.
314309
///
315310
/// - Parameters:
316311
/// - url: The authentication URL
317-
/// - rootViewController: The view controller to act as the presenter for the signin view controller.
318-
/// By convention this is the app's root vc.
312+
/// - rootViewController: The view controller to act as the presenter for the signin view controller. By convention this is the app's root vc.
313+
/// - automatedTesting: for calling this method for automated testing. It won't sync the account or load any other VCs.
319314
///
320-
@objc public class func openAuthenticationURL(_ url: URL, fromRootViewController rootViewController: UIViewController) -> Bool {
321-
guard let token = url.query?.dictionaryFromQueryString().string(forKey: "token") else {
322-
DDLogError("Signin Error: The authentication URL did not have the expected path.")
315+
@objc public class func openAuthenticationURL(
316+
_ url: URL,
317+
fromRootViewController rootViewController: UIViewController,
318+
automatedTesting: Bool = false) -> Bool {
319+
320+
guard let queryDictionary = url.query?.dictionaryFromQueryString() else {
321+
DDLogError("Magic link error: we couldn't retrieve the query dictionary from the sign-in URL.")
323322
return false
324323
}
325-
326-
let loginFields = retrieveLoginInfoForTokenAuth()
327-
324+
325+
guard let authToken = queryDictionary.string(forKey: "token") else {
326+
DDLogError("Magic link error: we couldn't retrieve the authentication token from the sign-in URL.")
327+
return false
328+
}
329+
330+
guard let flowRawValue = queryDictionary.string(forKey: "flow") else {
331+
DDLogError("Magic link error: we couldn't retrieve the flow from the sign-in URL.")
332+
return false
333+
}
334+
335+
let loginFields = LoginFields()
336+
328337
if url.isJetpackConnect {
329338
loginFields.meta.jetpackLogin = true
330339
}
331-
332-
let storyboard = Storyboard.emailMagicLink.instance
333-
guard let loginController = storyboard.instantiateViewController(withIdentifier: "LinkAuthView") as? NUXLinkAuthViewController else {
334-
DDLogInfo("App opened with authentication link but couldn't create login screen.")
340+
341+
// We could just use the flow, but since `MagicLinkFlow` is an ObjC enum, it always
342+
// allows a `default` value. By mapping the ObjC enum to a Swift enum we can avoid that afterwards.
343+
let flow: NUXLinkAuthViewController.Flow
344+
345+
switch MagicLinkFlow(rawValue: flowRawValue) {
346+
case .signup:
347+
flow = .signup
348+
loginFields.meta.emailMagicLinkSource = .signup
349+
Self.track(.signupMagicLinkOpened)
350+
case .login:
351+
flow = .login
352+
loginFields.meta.emailMagicLinkSource = .login
353+
Self.track(.loginMagicLinkOpened)
354+
default:
355+
DDLogError("Magic link error: the flow should be either `signup` or `login`. We can't handle an unsupported flow.")
335356
return false
336357
}
337-
loginController.loginFields = loginFields
338-
loginController.token = token
339-
let controller = loginController
340-
341-
if let linkSource = loginFields.meta.emailMagicLinkSource {
342-
switch linkSource {
343-
case .signup:
344-
WordPressAuthenticator.track(.signupMagicLinkOpened)
345-
case .login:
346-
WordPressAuthenticator.track(.loginMagicLinkOpened)
358+
359+
if !automatedTesting {
360+
let storyboard = Storyboard.emailMagicLink.instance
361+
guard let loginVC = storyboard.instantiateViewController(withIdentifier: "LinkAuthView") as? NUXLinkAuthViewController else {
362+
DDLogInfo("App opened with authentication link but couldn't create login screen.")
363+
return false
347364
}
365+
loginVC.loginFields = loginFields
366+
367+
let navController = LoginNavigationController(rootViewController: loginVC)
368+
navController.modalPresentationStyle = .fullScreen
369+
370+
// The way the magic link flow works some view controller might
371+
// still be presented when the app is resumed by tapping on the auth link.
372+
// We need to do a little work to present the SigninLinkAuth controller
373+
// from the right place.
374+
// - If the rootViewController is not presenting another vc then just
375+
// present the auth controller.
376+
// - If the rootViewController is presenting another NUX vc, dismiss the
377+
// NUX vc then present the auth controller.
378+
// - If the rootViewController is presenting *any* other vc, present the
379+
// auth controller from the presented vc.
380+
let presenter = rootViewController.topmostPresentedViewController
381+
if presenter.isKind(of: NUXNavigationController.self) || presenter.isKind(of: LoginNavigationController.self),
382+
let parent = presenter.presentingViewController {
383+
parent.dismiss(animated: false, completion: {
384+
parent.present(navController, animated: false, completion: nil)
385+
})
386+
} else {
387+
presenter.present(navController, animated: false, completion: nil)
388+
}
389+
390+
loginVC.syncAndContinue(authToken: authToken, flow: flow, isJetpackConnect: url.isJetpackConnect)
348391
}
349-
350-
let navController = LoginNavigationController(rootViewController: controller)
351-
navController.modalPresentationStyle = .fullScreen
352-
353-
// The way the magic link flow works some view controller might
354-
// still be presented when the app is resumed by tapping on the auth link.
355-
// We need to do a little work to present the SigninLinkAuth controller
356-
// from the right place.
357-
// - If the rootViewController is not presenting another vc then just
358-
// present the auth controller.
359-
// - If the rootViewController is presenting another NUX vc, dismiss the
360-
// NUX vc then present the auth controller.
361-
// - If the rootViewController is presenting *any* other vc, present the
362-
// auth controller from the presented vc.
363-
let presenter = rootViewController.topmostPresentedViewController
364-
if presenter.isKind(of: NUXNavigationController.self) || presenter.isKind(of: LoginNavigationController.self),
365-
let parent = presenter.presentingViewController {
366-
parent.dismiss(animated: false, completion: {
367-
parent.present(navController, animated: false, completion: nil)
368-
})
369-
} else {
370-
presenter.present(navController, animated: false, completion: nil)
371-
}
372-
373-
deleteLoginInfoForTokenAuth()
392+
374393
return true
375394
}
376395

@@ -409,58 +428,6 @@ import AuthenticationServices
409428
return path
410429
}
411430

412-
413-
// MARK: - Helpers for Saved Magic Link Info
414-
415-
/// Saves certain login information in NSUserDefaults
416-
///
417-
/// - Parameter loginFields: The loginFields instance from which to save.
418-
///
419-
class func storeLoginInfoForTokenAuth(_ loginFields: LoginFields) {
420-
var dict: [String: String] = [
421-
Constants.username: loginFields.username
422-
]
423-
424-
if let linkSource = loginFields.meta.emailMagicLinkSource {
425-
dict[Constants.emailMagicLinkSource] = String(linkSource.rawValue)
426-
}
427-
428-
UserDefaults.standard.set(dict, forKey: Constants.authenticationInfoKey)
429-
}
430-
431-
432-
/// Retrieves stored login information if any.
433-
///
434-
/// - Returns: A loginFields instance or nil.
435-
///
436-
class func retrieveLoginInfoForTokenAuth() -> LoginFields {
437-
438-
let loginFields = LoginFields()
439-
440-
guard let dict = UserDefaults.standard.dictionary(forKey: Constants.authenticationInfoKey) else {
441-
return loginFields
442-
}
443-
444-
if let username = dict[Constants.username] as? String {
445-
loginFields.username = username
446-
}
447-
448-
if let linkSource = dict[Constants.emailMagicLinkSource] as? String,
449-
let linkSourceRawValue = Int(linkSource) {
450-
loginFields.meta.emailMagicLinkSource = EmailMagicLinkSource(rawValue: linkSourceRawValue)
451-
}
452-
453-
return loginFields
454-
}
455-
456-
457-
/// Removes stored login information from NSUserDefaults
458-
///
459-
class func deleteLoginInfoForTokenAuth() {
460-
UserDefaults.standard.removeObject(forKey: Constants.authenticationInfoKey)
461-
}
462-
463-
464431
// MARK: - Other Helpers
465432

466433

WordPressAuthenticator/Credentials/WordPressComCredentials.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,19 @@ public struct WordPressComCredentials: Equatable {
1919
/// The site address used during login
2020
///
2121
public var siteURL: String
22-
23-
/// Designated initializer
22+
23+
private let wpComURL = "https://wordpress.com"
24+
25+
/// Legacy initializer, for backwards compatibility
2426
///
2527
public init(authToken: String,
2628
isJetpackLogin: Bool,
2729
multifactor: Bool,
28-
siteURL: String) {
30+
siteURL: String = "https://wordpress.com") {
2931
self.authToken = authToken
3032
self.isJetpackLogin = isJetpackLogin
3133
self.multifactor = multifactor
32-
self.siteURL = !siteURL.isEmpty ? siteURL : "https://wordpress.com"
34+
self.siteURL = !siteURL.isEmpty ? siteURL : wpComURL
3335
}
3436
}
3537

WordPressAuthenticator/NUX/NUXLinkAuthViewController.swift

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,28 @@ import WordPressShared
99
///
1010
class NUXLinkAuthViewController: LoginViewController {
1111
@IBOutlet weak var statusLabel: UILabel?
12-
@objc var token: String = ""
13-
@objc var didSync: Bool = false
14-
15-
override func viewDidAppear(_ animated: Bool) {
16-
super.viewDidAppear(animated)
17-
18-
// Gotta have a token to use this vc
19-
assert(!token.isEmpty, "Email token cannot be nil")
20-
21-
if didSync {
22-
return
23-
}
24-
25-
didSync = true // Make sure we don't call this twice by accident
12+
13+
enum Flow {
14+
case signup
15+
case login
16+
}
2617

27-
let wpcom = WordPressComCredentials(authToken: token, isJetpackLogin: isJetpackLogin, multifactor: false, siteURL: loginFields.siteAddress)
18+
/// Displays the specified text in the status label.
19+
///
20+
/// - Parameter message: The text to display in the label.
21+
///
22+
override func configureStatusLabel(_ message: String) {
23+
statusLabel?.text = message
24+
}
25+
26+
func syncAndContinue(authToken: String, flow: Flow, isJetpackConnect: Bool) {
27+
let wpcom = WordPressComCredentials(authToken: authToken, isJetpackLogin: isJetpackConnect, multifactor: false, siteURL: "https://wordpress.com")
2828
let credentials = AuthenticatorCredentials(wpcom: wpcom)
2929

30-
tracker.track(step: .success)
31-
syncWPComAndPresentEpilogue(credentials: credentials)
32-
33-
// Count this as success since we're authed. Even if there is a glitch
34-
// while syncing the user has valid credentials.
35-
if let linkSource = loginFields.meta.emailMagicLinkSource {
36-
switch linkSource {
30+
syncWPComAndPresentEpilogue(credentials: credentials) {
31+
self.tracker.track(step: .success)
32+
33+
switch flow {
3734
case .signup:
3835
// This stat is part of a funnel that provides critical information. Before
3936
// making ANY modification to this stat please refer to: p4qSXL-35X-p2
@@ -44,12 +41,4 @@ class NUXLinkAuthViewController: LoginViewController {
4441
}
4542
}
4643
}
47-
48-
/// Displays the specified text in the status label.
49-
///
50-
/// - Parameter message: The text to display in the label.
51-
///
52-
override func configureStatusLabel(_ message: String) {
53-
statusLabel?.text = message
54-
}
5544
}

WordPressAuthenticator/Services/WordPressComAccountService.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class WordPressComAccountService {
5151
remote.requestWPComAuthLink(forEmail: email,
5252
clientID: configuration.wpcomClientId,
5353
clientSecret: configuration.wpcomSecret,
54-
jetpackLogin: jetpackLogin,
54+
source: jetpackLogin ? .jetpackConnect : .default,
5555
wpcomScheme: configuration.wpcomScheme,
5656
success: success,
5757
failure: { error in

WordPressAuthenticator/Signin/LoginLinkRequestViewController.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ class LoginLinkRequestViewController: LoginViewController {
154154

155155
@objc func didRequestAuthenticationLink() {
156156
WordPressAuthenticator.track(.loginMagicLinkRequested)
157-
WordPressAuthenticator.storeLoginInfoForTokenAuth(loginFields)
158157

159158
guard let vc = NUXLinkMailViewController.instantiate(from: .emailMagicLink) else {
160159
DDLogError("Failed to navigate to NUXLinkMailViewController")

0 commit comments

Comments
 (0)