Compare commits

...
1 Commits
Author SHA1 Message Date
Max Goedjen 9edc879900 Fix bug where LAContext will be evaluated with incorrect policy for biometricCurrent secrets (#845)
* Fix bug where LAContext will be evaluated with deviceOwnerAuthentication instead of deviceOwnerAuthenticationWithBiometrics

* Fix test
2026-10-01 00:02:40 -05:00
3 changed files with 8 additions and 8 deletions
@@ -73,9 +73,10 @@ public final class AuthenticationContext: AuthenticationContextProtocol {
} }
} }
public func evaluate() async throws -> Bool { public func evaluate(requireBiometrics: Bool) async throws -> Bool {
guard let laContext else { return false } guard let laContext else { return false }
return try await laContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: laContext.localizedReason) let policy: LAPolicy = requireBiometrics ? .deviceOwnerAuthenticationWithBiometrics : .deviceOwnerAuthentication
return try await laContext.evaluatePolicy(policy, localizedReason: laContext.localizedReason)
} }
public func cancel() async { public func cancel() async {
@@ -141,7 +142,7 @@ public final class AuthenticationContext: AuthenticationContextProtocol {
activeContext = context activeContext = context
let currentTask = Task<Bool, any Error> { let currentTask = Task<Bool, any Error> {
logger.log("Beginning individual auth prompt") logger.log("Beginning individual auth prompt")
let result = (try? await context.evaluate()) ?? false let result = (try? await context.evaluate(requireBiometrics: request.secret.authenticationRequirement == .biometryCurrent)) ?? false
logger.log("Ended individual auth prompt") logger.log("Ended individual auth prompt")
return result return result
} }
@@ -195,7 +196,7 @@ public final class AuthenticationContext: AuthenticationContextProtocol {
public func persistAuthentication<SecretType: Secret>(secret: SecretType, forDuration duration: TimeInterval) async throws { public func persistAuthentication<SecretType: Secret>(secret: SecretType, forDuration duration: TimeInterval) async throws {
let context = AuthenticationContext(secret: secret, duration: duration) let context = AuthenticationContext(secret: secret, duration: duration)
let success = try await context.evaluate() let success = try await context.evaluate(requireBiometrics: secret.authenticationRequirement == .biometryCurrent)
guard success else { return } guard success else { return }
authenticatedContexts[AnySecret(secret)] = context authenticatedContexts[AnySecret(secret)] = context
} }
@@ -204,7 +205,7 @@ public final class AuthenticationContext: AuthenticationContextProtocol {
activeTask?.cancel() activeTask?.cancel()
guard let first = requests.first else { return } guard let first = requests.first else { return }
let context = AuthenticationContext(secret: first.secret, requests: requests) let context = AuthenticationContext(secret: first.secret, requests: requests)
let success = (try? await context.evaluate()) ?? false let success = (try? await context.evaluate(requireBiometrics: first.secret.authenticationRequirement == .biometryCurrent)) ?? false
guard success else { guard success else {
waitingRequests.subtract(requests) waitingRequests.subtract(requests)
return return
@@ -6,7 +6,7 @@ public protocol AuthenticationContextProtocol: Sendable, Identifiable {
var secret: AnySecret { get } var secret: AnySecret { get }
func valid(for request: SignatureRequest) -> Bool func valid(for request: SignatureRequest) -> Bool
var laContext: LAContext? { get } var laContext: LAContext? { get }
func evaluate() async throws -> Bool func evaluate(requireBiometrics: Bool) async throws -> Bool
func cancel() async func cancel() async
} }
@@ -42,8 +42,7 @@ public struct TestSecret: Secret {
true true
} }
func evaluate(requireBiometrics: Bool) async throws -> Bool {
func evaluate() async throws -> Bool {
if case let .duration(duration) = approval { if case let .duration(duration) = approval {
try await Task.sleep(for: duration) try await Task.sleep(for: duration)
} else { } else {