Skip to content

Commit

Permalink
Merge pull request #215 from line/fix/login-type
Browse files Browse the repository at this point in the history
Feature login route property
  • Loading branch information
onevcat authored Oct 30, 2024
2 parents 7c0de94 + 82bb833 commit 35dfe7b
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 11 deletions.
3 changes: 2 additions & 1 deletion LineSDK/LineSDK/Login/LoginManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,8 @@ public class LoginManager {
permissions: Set(token.permissions),
userProfile: profile,
friendshipStatusChanged: response.friendshipStatusChanged,
IDTokenNonce: process.IDTokenNonce)
IDTokenNonce: process.IDTokenNonce
)
}
completion(result)
}
Expand Down
47 changes: 43 additions & 4 deletions LineSDK/LineSDK/Login/LoginProcess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ import SafariServices
/// login flows will run serially. If a flow logs in the user successfully, subsequent flows will not be
/// executed.
public class LoginProcess {

/// Represents a login route for how the auth flow is initiated.
public enum LoginRoute: String {
/// The auth flow starts with a LINE app universal link.
case appUniversalLink
/// The auth flow starts with a LINE customize URL scheme.
case appAuthScheme
/// The auth flow starts in a web page inside LINE SDK.
case webLogin
}

struct FlowParameters {
let channelID: String
let universalLinkURL: URL?
Expand Down Expand Up @@ -75,24 +86,52 @@ public class LoginProcess {
let configuration: LoginConfiguration
let scopes: Set<LoginPermission>
let parameters: LoginManager.Parameters

// Flows of login process. A flow will be `nil` until it is running, so we could tell which one should take
// responsibility to handle a url callback response.

// LINE Client app auth flow captured by LINE universal link.
var appUniversalLinkFlow: AppUniversalLinkFlow?
var appUniversalLinkFlow: AppUniversalLinkFlow? {
didSet {
if appUniversalLinkFlow != nil && loginRoute == nil {
loginRoute = .appUniversalLink
}
}
}
// LINE Client app auth flow by LINE customize URL scheme.
var appAuthSchemeFlow: AppAuthSchemeFlow?
var appAuthSchemeFlow: AppAuthSchemeFlow? {
didSet {
if appAuthSchemeFlow != nil && loginRoute == nil {
loginRoute = .appAuthScheme
}
}
}

// Web login flow with Safari View Controller or Mobile Safari
var webLoginFlow: WebLoginFlow? {
didSet {
// Dismiss safari view controller (if exists) when reset web login flow.
if webLoginFlow == nil {
oldValue?.dismiss()
}

if webLoginFlow != nil && loginRoute == nil {
loginRoute = .webLogin
}
}
}


/// Describes how the authentication flow is initiated for this login result.
///
/// If the LINE app was launched to obtain this result, the value will be either `.appUniversalLink` or
/// `.appAuthScheme`, depending on how the LINE app was opened. If authentication occurred via a web page within
/// the LINE SDK, the value will be `.webLogin`. If the authentication flow is never or not yet initiated, the value
/// will be `nil`.
///
/// This value is `nil` until the process starts the auth flow actually. You can access this value safely when an
/// auth result is retrieved.
public private(set) var loginRoute: LoginRoute?

// When we leave current app, we need to set the switching observer
// to intercept cancel event (switching back but without a token url response)
var appSwitchingObserver: AppSwitchingObserver?
Expand Down
2 changes: 2 additions & 0 deletions LineSDK/LineSDK/Login/LoginResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import Foundation

/// Represents a successful login.
public struct LoginResult {

/// The access token obtained by the login process.
public let accessToken: AccessToken
/// The permissions bound to the `accessToken` object by the authorization process.
Expand All @@ -48,6 +49,7 @@ extension LoginResult: Encodable {
case userProfile
case friendshipStatusChanged
case IDTokenNonce
case loginRoute
}

/// :nodoc:
Expand Down
4 changes: 3 additions & 1 deletion LineSDK/LineSDKObjC/Login/LineSDKLoginProcess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
public class LineSDKLoginProcess: NSObject {
let _value: LoginProcess
init(_ value: LoginProcess) { _value = value }


public var loginRoute: String? { return _value.loginRoute?.rawValue }

public func stop() { _value.stop() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ - (void)testLoginResultInterface {

- (void)testLoginProcessInterface {
LineSDKLoginProcess *process = nil;
XCTAssertNil(process.loginRoute);
[process stop];
}

Expand Down
59 changes: 54 additions & 5 deletions LineSDK/LineSDKTests/Login/LoginManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@
import XCTest
@testable import LineSDK

let sampleFlowParameters = LoginProcess.FlowParameters(
channelID: "",
universalLinkURL: nil,
scopes: [],
pkce: .init(),
processID: "",
nonce: nil,
botPrompt: nil,
preferredWebPageLanguage: nil,
onlyWebLogin: false,
promptBotID: nil
)

class LoginManagerTests: XCTestCase, ViewControllerCompatibleTest {

var window: UIWindow!
Expand Down Expand Up @@ -60,8 +73,9 @@ class LoginManagerTests: XCTestCase, ViewControllerCompatibleTest {
configuration: LoginConfiguration.shared,
delegate: delegateStub
)

let process = LoginManager.shared.login(permissions: [.profile], in: setupViewController()) {

var process: LoginProcess!
process = LoginManager.shared.login(permissions: [.profile], in: setupViewController()) {
loginResult in
XCTAssertNotNil(loginResult.value)

Expand All @@ -74,16 +88,22 @@ class LoginManagerTests: XCTestCase, ViewControllerCompatibleTest {

// IDTokenNonce should be `nil` when `.openID` not required.
XCTAssertNil(result.IDTokenNonce)


XCTAssertEqual(process.loginRoute, .appUniversalLink)

try! AccessTokenStore.shared.removeCurrentAccessToken()
expect.fulfill()
}!


// Set a sample value for checking `loginRoute` in the result.
process.appUniversalLinkFlow = AppUniversalLinkFlow(parameter: sampleFlowParameters)

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {

XCTAssertFalse(LoginManager.shared.isAuthorized)
XCTAssertTrue(LoginManager.shared.isAuthorizing)


// Simulate auth result
let urlString = "\(Constant.thirdPartyAppReturnURL)?code=123&state=\(process.processID)"
let handled = process.resumeOpenURL(url: URL(string: urlString)!)
XCTAssertTrue(handled)
Expand Down Expand Up @@ -173,5 +193,34 @@ class LoginManagerTests: XCTestCase, ViewControllerCompatibleTest {
waitForExpectations(timeout: 1, handler: nil)
}

func testLoginProcessRouteSetting() {
XCTContext.runActivity(named: "app universal link") { _ in
let process = LoginProcess(
configuration: .shared, scopes: [], parameters: .init(), viewController: setupViewController()
)
XCTAssertNil(process.loginRoute)
process.appUniversalLinkFlow = AppUniversalLinkFlow(parameter: sampleFlowParameters)
XCTAssertEqual(process.loginRoute, .appUniversalLink)
}

XCTContext.runActivity(named: "app auth") { _ in
let process = LoginProcess(
configuration: .shared, scopes: [], parameters: .init(), viewController: setupViewController()
)
XCTAssertNil(process.loginRoute)
process.appAuthSchemeFlow = AppAuthSchemeFlow(parameter: sampleFlowParameters)
XCTAssertEqual(process.loginRoute, .appAuthScheme)
}

XCTContext.runActivity(named: "web login") { _ in
let process = LoginProcess(
configuration: .shared, scopes: [], parameters: .init(), viewController: setupViewController()
)
XCTAssertNil(process.loginRoute)
process.webLoginFlow = WebLoginFlow(parameter: sampleFlowParameters)
XCTAssertEqual(process.loginRoute, .webLogin)
}
}

}

0 comments on commit 35dfe7b

Please sign in to comment.