Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/local_auth/local_auth/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ dev_dependencies:

flutter:
uses-material-design: true
# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
local_auth_darwin: {path: ../../../../packages/local_auth/local_auth_darwin}
4 changes: 4 additions & 0 deletions packages/local_auth/local_auth/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ topics:
- authentication
- biometrics
- local-auth
# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins
dependency_overrides:
local_auth_darwin: {path: ../../../packages/local_auth/local_auth_darwin}
3 changes: 2 additions & 1 deletion packages/local_auth/local_auth_darwin/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 2.1.0

* Add `stopAuthentication` implementation.
* Updates minimum supported SDK version to Flutter 3.38/Dart 3.10.

## 2.0.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ final class StubAuthContext: NSObject, AuthContext, @unchecked Sendable {
var biometryType: LABiometryType = .none
var localizedFallbackTitle: String?

/// Tracks if invalidate() was called during the test.
var invalidated = false

func canEvaluatePolicy(_ policy: LAPolicy, error: NSErrorPointer) -> Bool {
#expect(
policy
Expand Down Expand Up @@ -75,6 +78,8 @@ final class StubAuthContext: NSObject, AuthContext, @unchecked Sendable {
reply(self.evaluateResponse, self.evaluateError)
}
}

func invalidate() { invalidated = true }
}

// MARK: -
Expand Down Expand Up @@ -454,6 +459,53 @@ struct LocalAuthPluginTests {
#expect(!result)
}

@Test
func stopAuthenticationHandlesNoActiveContext() async {
let stubAuthContext = StubAuthContext()
let plugin = LocalAuthPlugin(
contextFactory: StubAuthContextFactory(contexts: [stubAuthContext]))

let result = await withCheckedContinuation { continuation in
plugin.stopAuthentication { response in
switch response {
case .success(let val):
continuation.resume(returning: val)
case .failure:
continuation.resume(returning: false)
}
}
}

#expect(!result)
#expect(!stubAuthContext.invalidated)
}

@Test
func stopAuthenticationHandlesActiveContext() async {
let stubAuthContext = StubAuthContext()
let plugin = LocalAuthPlugin(
contextFactory: StubAuthContextFactory(contexts: [stubAuthContext]))

plugin.authenticate(
options: AuthOptions(biometricOnly: false, sticky: false),
strings: createAuthStrings()
) { _ in }

let result = await withCheckedContinuation { continuation in
plugin.stopAuthentication { response in
switch response {
case .success(let val):
continuation.resume(returning: val)
case .failure:
continuation.resume(returning: false)
}
}
}

#expect(result)
#expect(stubAuthContext.invalidated)
}

// Creates an AuthStrings with placeholder values.
func createAuthStrings(localizedFallbackTitle: String? = nil) -> AuthStrings {
return AuthStrings(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public final class LocalAuthPlugin: NSObject, FlutterPlugin, LocalAuthApi, @unch
private let authContextFactory: AuthContextFactory
/// Manages the last call state for sticky auth.
private var lastCallState: StickyAuthState?
/// The active context for the current authentication session.
private var activeContext: AuthContext?

public static func register(with registrar: FlutterPluginRegistrar) {
let instance = LocalAuthPlugin(
Expand Down Expand Up @@ -74,6 +76,7 @@ public final class LocalAuthPlugin: NSObject, FlutterPlugin, LocalAuthApi, @unch
completion: @escaping (Result<AuthResultDetails, Error>) -> Void
) {
var context = authContextFactory.createAuthContext()
activeContext = context
lastCallState = nil
context.localizedFallbackTitle = strings.localizedFallbackTitle

Expand All @@ -88,6 +91,11 @@ public final class LocalAuthPlugin: NSObject, FlutterPlugin, LocalAuthApi, @unch
localizedReason: strings.reason
) { [weak self] (success: Bool, error: Error?) in
DispatchQueue.main.async {
if let currentActive = self?.activeContext,
currentActive as AnyObject === context as AnyObject
{
self?.activeContext = nil
}
self?.handleAuthReply(
success: success,
error: error,
Expand All @@ -97,6 +105,7 @@ public final class LocalAuthPlugin: NSObject, FlutterPlugin, LocalAuthApi, @unch
}
}
} else {
activeContext = nil
if let authError = authError {
self.handleError(authError, options: options, strings: strings, completion: completion)
} else {
Expand Down Expand Up @@ -159,6 +168,17 @@ public final class LocalAuthPlugin: NSObject, FlutterPlugin, LocalAuthApi, @unch
return context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
}

func stopAuthentication(completion: @escaping (Result<Bool, any Error>) -> Void) {
guard let context = activeContext else {
completion(.success(false))
return
}

context.invalidate()
activeContext = nil
completion(.success(true))
}

// MARK: Private Methods

private func handleAuthReply(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ protocol AuthContext {
localizedReason: String,
reply: @escaping @Sendable (Bool, Error?) -> Void
)

/// Direct passthrough to LAContext's invalidate.
func invalidate()
}

/// AuthContext is intentionally a direct passthroguh to LAContext.
Expand Down
Loading