Skip to content

Commit aafe920

Browse files
authored
Merge pull request #219 from line/feature/initial-qr-screen
Add a parameter for setting `initialWebAuthenticationMethod`
2 parents 4f57278 + d6aa95c commit aafe920

File tree

7 files changed

+145
-37
lines changed

7 files changed

+145
-37
lines changed

LineSDK/LineSDK/Login/LoginManagerParameters.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ extension LoginManager {
6363
#else
6464
public var allowRecreatingLoginProcess = false
6565
#endif
66+
67+
/// Specifies the initial web authentication method to be used when starting the login process.
68+
///
69+
/// By default, `.email` is used to provide input boxes for email and password.
70+
public var initialWebAuthenticationMethod: WebAuthenticationMethod = .email
6671

6772
/// Creates a default `LoginManager.Parameters` value.
6873
public init() {}
@@ -89,7 +94,13 @@ extension LoginManager {
8994
/// consent screen.
9095
case aggressive
9196
}
92-
97+
98+
/// The method used for the authentication when using the web authentication flow.
99+
public enum WebAuthenticationMethod: String {
100+
case email
101+
case qrCode
102+
}
103+
93104
/// Represents the language used in the web page.
94105
public struct WebPageLanguage {
95106
/// :nodoc:

LineSDK/LineSDK/Login/LoginProcess.swift

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,21 @@ public class LoginProcess {
4545
let pkce: PKCE
4646
let processID: String
4747
let nonce: String?
48-
let botPrompt: LoginManager.BotPrompt?
49-
let preferredWebPageLanguage: LoginManager.WebPageLanguage?
50-
let onlyWebLogin: Bool
51-
let promptBotID: String?
48+
49+
let loginParameter: LoginManager.Parameters
50+
51+
var botPrompt: LoginManager.BotPrompt? {
52+
loginParameter.botPromptStyle
53+
}
54+
var preferredWebPageLanguage: LoginManager.WebPageLanguage? {
55+
loginParameter.preferredWebPageLanguage
56+
}
57+
var onlyWebLogin: Bool {
58+
loginParameter.onlyWebLogin
59+
}
60+
var promptBotID: String? {
61+
loginParameter.promptBotID
62+
}
5263
}
5364

5465
/// Observes application switching to foreground.
@@ -177,10 +188,7 @@ public class LoginProcess {
177188
pkce: pkce,
178189
processID: processID,
179190
nonce: IDTokenNonce,
180-
botPrompt: parameters.botPromptStyle,
181-
preferredWebPageLanguage: parameters.preferredWebPageLanguage,
182-
onlyWebLogin: parameters.onlyWebLogin,
183-
promptBotID: parameters.promptBotID
191+
loginParameter: parameters
184192
)
185193
#if targetEnvironment(macCatalyst)
186194
// On macCatalyst, we only support web login
@@ -406,8 +414,15 @@ class WebLoginFlow: NSObject {
406414
weak var safariViewController: UIViewController?
407415

408416
init(parameter: LoginProcess.FlowParameters) {
409-
let webLoginURLBase = URL(string: Constant.lineWebAuthURL)!
410-
url = webLoginURLBase.appendedLoginQuery(parameter)
417+
var component = URLComponents(string: Constant.lineWebAuthURL)!
418+
if parameter.loginParameter.initialWebAuthenticationMethod == .qrCode {
419+
if let _ = component.fragment {
420+
assertionFailure("Multiple fragment is not yet supported. Require review or report to developer.")
421+
}
422+
component.fragment = "/qr"
423+
}
424+
let baseURL = component.url!
425+
url = baseURL.appendedLoginQuery(parameter)
411426
}
412427

413428
func start(in viewController: UIViewController?) {

LineSDK/LineSDKObjC/Login/LineSDKLoginManagerParameters.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,17 @@ public class LineSDKLoginManagerParameters: NSObject {
4444
get { return _value.promptBotID }
4545
set { _value.promptBotID = newValue }
4646
}
47-
47+
48+
public var initialWebAuthenticationMethod: LineSDKLoginManagerWebAuthenticationMethod {
49+
get { return LineSDKLoginManagerWebAuthenticationMethod(_value.initialWebAuthenticationMethod) }
50+
set { _value.initialWebAuthenticationMethod = newValue._value }
51+
}
52+
4853
public var preferredWebPageLanguage: String? {
4954
get { return _value.preferredWebPageLanguage?.rawValue }
5055
set { _value.preferredWebPageLanguage = newValue.map { .init(rawValue: $0) } }
5156
}
52-
57+
5358
public var IDTokenNonce: String? {
5459
get { return _value.IDTokenNonce }
5560
set { _value.IDTokenNonce = newValue }
@@ -67,3 +72,15 @@ public class LineSDKLoginManagerBotPrompt: NSObject {
6772

6873
public var rawValue: String { return _value.rawValue }
6974
}
75+
76+
@objcMembers
77+
public class LineSDKLoginManagerWebAuthenticationMethod: NSObject {
78+
79+
let _value: LoginManager.WebAuthenticationMethod
80+
init(_ value: LoginManager.WebAuthenticationMethod) { _value = value }
81+
82+
public static let email = LineSDKLoginManagerWebAuthenticationMethod(.email)
83+
public static let qrCode = LineSDKLoginManagerWebAuthenticationMethod(.qrCode)
84+
85+
public var rawValue: String { return _value.rawValue }
86+
}

LineSDK/LineSDKObjCInterfaceTests/LineSDKModelInterfaceTests.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,13 @@ - (void)testLoginManagerParametersInterface {
104104
param.preferredWebPageLanguage = @"ja";
105105
param.IDTokenNonce = @"test";
106106
param.promptBotID = @"@abc123";
107-
107+
param.initialWebAuthenticationMethod = [LineSDKLoginManagerWebAuthenticationMethod qrCode];
108+
108109
XCTAssertTrue([param onlyWebLogin]);
109110
XCTAssertTrue([[param.botPromptStyle rawValue] isEqualToString: @"normal"]);
110111
XCTAssertTrue([param.preferredWebPageLanguage isEqualToString: @"ja"]);
111112
XCTAssertTrue([param.IDTokenNonce isEqualToString: @"test"]);
113+
XCTAssertTrue([[param.initialWebAuthenticationMethod rawValue] isEqualToString:@"qrCode"]);
112114
}
113115

114116
- (void)testLoginResultInterface {

LineSDK/LineSDKTests/Login/LoginFlowTests.swift

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ class LoginFlowTests: XCTestCase, ViewControllerCompatibleTest {
3333
pkce: PKCE(),
3434
processID: "abc",
3535
nonce: "kkk",
36-
botPrompt: .normal,
37-
preferredWebPageLanguage: nil,
38-
onlyWebLogin: false,
39-
promptBotID: nil
36+
loginParameter: {
37+
var p = LoginManager.Parameters()
38+
p.botPromptStyle = .normal
39+
return p
40+
}()
4041
)
4142

4243
let parameterWithLanguage = LoginProcess.FlowParameters(
@@ -46,10 +47,12 @@ class LoginFlowTests: XCTestCase, ViewControllerCompatibleTest {
4647
pkce: PKCE(),
4748
processID: "abc",
4849
nonce: "kkk",
49-
botPrompt: .normal,
50-
preferredWebPageLanguage: .chineseSimplified,
51-
onlyWebLogin: false,
52-
promptBotID: nil
50+
loginParameter: {
51+
var p = LoginManager.Parameters()
52+
p.botPromptStyle = .normal
53+
p.preferredWebPageLanguage = .chineseSimplified
54+
return p
55+
}()
5356
)
5457

5558
let parameterWithOnlyWebLogin = LoginProcess.FlowParameters(
@@ -59,10 +62,12 @@ class LoginFlowTests: XCTestCase, ViewControllerCompatibleTest {
5962
pkce: PKCE(),
6063
processID: "abc",
6164
nonce: "kkk",
62-
botPrompt: .normal,
63-
preferredWebPageLanguage: nil,
64-
onlyWebLogin: true,
65-
promptBotID: nil
65+
loginParameter: {
66+
var p = LoginManager.Parameters()
67+
p.botPromptStyle = .normal
68+
p.onlyWebLogin = true
69+
return p
70+
}()
6671
)
6772

6873
let parameterWithPromptBotID = LoginProcess.FlowParameters(
@@ -72,12 +77,29 @@ class LoginFlowTests: XCTestCase, ViewControllerCompatibleTest {
7277
pkce: PKCE(),
7378
processID: "abc",
7479
nonce: "kkk",
75-
botPrompt: .normal,
76-
preferredWebPageLanguage: nil,
77-
onlyWebLogin: false,
78-
promptBotID: "@abc123"
80+
loginParameter: {
81+
var p = LoginManager.Parameters()
82+
p.botPromptStyle = .normal
83+
p.promptBotID = "@abc123"
84+
return p
85+
}()
7986
)
80-
87+
88+
let parameterWithInitialQRMethod = LoginProcess.FlowParameters(
89+
channelID: "123",
90+
universalLinkURL: nil,
91+
scopes: [.profile, .openID],
92+
pkce: PKCE(),
93+
processID: "abc",
94+
nonce: "kkk",
95+
loginParameter: {
96+
var p = LoginManager.Parameters()
97+
p.botPromptStyle = .normal
98+
p.initialWebAuthenticationMethod = .qrCode
99+
return p
100+
}()
101+
)
102+
81103
// Login URL has a double escaped query.
82104
func testLoginQueryURLEncode() {
83105

@@ -171,7 +193,7 @@ class LoginFlowTests: XCTestCase, ViewControllerCompatibleTest {
171193
XCTAssertNotEqual(item.value, item.value?.removingPercentEncoding)
172194
XCTAssertTrue(item.value!.removingPercentEncoding!.contains("prompt_bot_id=@abc123"))
173195
}
174-
196+
175197
// URL Scheme has a triple escaped query.
176198
func testURLSchemeQueryEncode() {
177199
let baseURL = Constant.lineAppAuthURLv2
@@ -252,7 +274,36 @@ class LoginFlowTests: XCTestCase, ViewControllerCompatibleTest {
252274
flow.start(in: rootViewController)
253275
waitForExpectations(timeout: 1.0, handler: nil)
254276
}
255-
277+
278+
func testWebLoginFlowWithQRCodeFirst() {
279+
let expect = expectation(description: "\(#file)_\(#line)")
280+
let flow = WebLoginFlow(parameter: parameterWithInitialQRMethod)
281+
let webURL = URL(string: Constant.lineWebAuthURL)!
282+
let components = URLComponents(url: flow.url, resolvingAgainstBaseURL: false)
283+
XCTAssertEqual(components?.scheme, "https")
284+
XCTAssertEqual(components?.host, webURL.host)
285+
XCTAssertEqual(components?.path, webURL.path)
286+
287+
XCTAssertEqual(components?.fragment, "/qr")
288+
XCTAssertTrue(flow.url.absoluteString.contains("#/qr"))
289+
290+
let rootViewController = setupViewController()
291+
292+
flow.onNext.delegate(on: self) { [unowned flow] (self, next) in
293+
expect.fulfill()
294+
self.resetViewController()
295+
switch next {
296+
case .safariViewController:
297+
XCTAssertEqual(rootViewController.presentedViewController, flow.safariViewController)
298+
default:
299+
XCTFail("Should present a safari web view controller.")
300+
}
301+
}
302+
303+
flow.start(in: rootViewController)
304+
waitForExpectations(timeout: 1.0, handler: nil)
305+
}
306+
256307
func testAppSwitchingObserver() {
257308
let expect = expectation(description: "\(#file)_\(#line)")
258309
let observer = LoginProcess.AppSwitchingObserver()

LineSDK/LineSDKTests/Login/LoginManagerTests.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,7 @@ let sampleFlowParameters = LoginProcess.FlowParameters(
2929
pkce: .init(),
3030
processID: "",
3131
nonce: nil,
32-
botPrompt: nil,
33-
preferredWebPageLanguage: nil,
34-
onlyWebLogin: false,
35-
promptBotID: nil
32+
loginParameter: .init()
3633
)
3734

3835
class LoginManagerTests: XCTestCase, ViewControllerCompatibleTest {

LineSDKSample/LineSDKSample/Login/LoginSettingsViewController.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,20 @@ class LoginSettingsViewController: UITableViewController {
8888
case .none: p.botPromptStyle = .aggressive
8989
}
9090
}
91+
),
92+
ParameterItem(
93+
title: "Initial Auth Method",
94+
text: { p in
95+
switch p.initialWebAuthenticationMethod {
96+
case .email: return "Email"
97+
case .qrCode: return "QR Code"
98+
}
99+
}, action: { p in
100+
switch p.initialWebAuthenticationMethod {
101+
case .email: p.initialWebAuthenticationMethod = .qrCode
102+
case .qrCode: p.initialWebAuthenticationMethod = .email
103+
}
104+
}
91105
)
92106
]
93107

@@ -137,6 +151,7 @@ class LoginSettingsViewController: UITableViewController {
137151
let p = parameters[indexPath.row]
138152
cell.textLabel?.text = p.title
139153
cell.detailTextLabel?.text = p.text(loginSettings.parameters)
154+
cell.accessoryType = .none
140155
}
141156

142157
return cell

0 commit comments

Comments
 (0)