Skip to content

Commit 9c34c74

Browse files
LouiseHsumauricioluz
authored andcommitted
[local_auth] Fix failed biometric authentication not throwing error (flutter#6821)
* fix failed biometric authentication not throwing error + tests * fix test names * Revert "fix test names" This reverts commit 89ba69c. * Revert "fix failed biometric authentication not throwing error + tests" This reverts commit 684790a. * fix authentication not throwing error + tests * fix test name * auto format * cr fixes * addressed pr comments * formatting * formatting * formatting * format attempt * format attempt * formatting fixes * change incorrect versionning * fix test * add back macro * fixed up tests, removed unnecessary assertions, replaced isAMemberOf * add back error for unknown error codes * tests * changed enum to something thats not deprecated * remove redundant test
1 parent 25ebe99 commit 9c34c74

File tree

4 files changed

+95
-18
lines changed

4 files changed

+95
-18
lines changed

packages/local_auth/local_auth_ios/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.0.11
2+
3+
* Fixes issue where failed authentication was failing silently
4+
15
## 1.0.10
26

37
* Updates imports for `prefer_relative_imports`.

packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m

Lines changed: 82 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ - (void)testFailedAuthWithBiometrics {
124124
void (^reply)(BOOL, NSError *);
125125
[invocation getArgument:&reply atIndex:4];
126126
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
127-
reply(NO, [NSError errorWithDomain:@"error" code:99 userInfo:nil]);
127+
reply(NO, [NSError errorWithDomain:@"error" code:LAErrorAuthenticationFailed userInfo:nil]);
128128
});
129129
};
130130
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:reason reply:[OCMArg any]])
@@ -140,14 +140,13 @@ - (void)testFailedAuthWithBiometrics {
140140
[plugin handleMethodCall:call
141141
result:^(id _Nullable result) {
142142
XCTAssertTrue([NSThread isMainThread]);
143-
XCTAssertTrue([result isKindOfClass:[NSNumber class]]);
144-
XCTAssertFalse([result boolValue]);
143+
XCTAssertTrue([result isKindOfClass:[FlutterError class]]);
145144
[expectation fulfill];
146145
}];
147146
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
148147
}
149148

150-
- (void)testFailedAuthWithoutBiometrics {
149+
- (void)testFailedWithUnknownErrorCode {
151150
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init];
152151
id mockAuthContext = OCMClassMock([LAContext class]);
153152
plugin.authContextOverrides = @[ mockAuthContext ];
@@ -175,6 +174,45 @@ - (void)testFailedAuthWithoutBiometrics {
175174
@"localizedReason" : reason,
176175
}];
177176

177+
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
178+
[plugin handleMethodCall:call
179+
result:^(id _Nullable result) {
180+
XCTAssertTrue([NSThread isMainThread]);
181+
XCTAssertTrue([result isKindOfClass:[FlutterError class]]);
182+
[expectation fulfill];
183+
}];
184+
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
185+
}
186+
187+
- (void)testSystemCancelledWithoutStickyAuth {
188+
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init];
189+
id mockAuthContext = OCMClassMock([LAContext class]);
190+
plugin.authContextOverrides = @[ mockAuthContext ];
191+
192+
const LAPolicy policy = LAPolicyDeviceOwnerAuthentication;
193+
NSString *reason = @"a reason";
194+
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
195+
196+
// evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not
197+
// guaranteed to be on the main thread. Ensure that's handled correctly by calling back on
198+
// a background thread.
199+
void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) {
200+
void (^reply)(BOOL, NSError *);
201+
[invocation getArgument:&reply atIndex:4];
202+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
203+
reply(NO, [NSError errorWithDomain:@"error" code:LAErrorSystemCancel userInfo:nil]);
204+
});
205+
};
206+
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:reason reply:[OCMArg any]])
207+
.andDo(backgroundThreadReplyCaller);
208+
209+
FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"authenticate"
210+
arguments:@{
211+
@"biometricOnly" : @(NO),
212+
@"localizedReason" : reason,
213+
@"stickyAuth" : @(NO)
214+
}];
215+
178216
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
179217
[plugin handleMethodCall:call
180218
result:^(id _Nullable result) {
@@ -186,6 +224,44 @@ - (void)testFailedAuthWithoutBiometrics {
186224
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
187225
}
188226

227+
- (void)testFailedAuthWithoutBiometrics {
228+
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init];
229+
id mockAuthContext = OCMClassMock([LAContext class]);
230+
plugin.authContextOverrides = @[ mockAuthContext ];
231+
232+
const LAPolicy policy = LAPolicyDeviceOwnerAuthentication;
233+
NSString *reason = @"a reason";
234+
OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES);
235+
236+
// evaluatePolicy:localizedReason:reply: calls back on an internal queue, which is not
237+
// guaranteed to be on the main thread. Ensure that's handled correctly by calling back on
238+
// a background thread.
239+
void (^backgroundThreadReplyCaller)(NSInvocation *) = ^(NSInvocation *invocation) {
240+
void (^reply)(BOOL, NSError *);
241+
[invocation getArgument:&reply atIndex:4];
242+
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
243+
reply(NO, [NSError errorWithDomain:@"error" code:LAErrorAuthenticationFailed userInfo:nil]);
244+
});
245+
};
246+
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:reason reply:[OCMArg any]])
247+
.andDo(backgroundThreadReplyCaller);
248+
249+
FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"authenticate"
250+
arguments:@{
251+
@"biometricOnly" : @(NO),
252+
@"localizedReason" : reason,
253+
}];
254+
255+
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
256+
[plugin handleMethodCall:call
257+
result:^(id _Nullable result) {
258+
XCTAssertTrue([NSThread isMainThread]);
259+
XCTAssertTrue([result isKindOfClass:[FlutterError class]]);
260+
[expectation fulfill];
261+
}];
262+
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
263+
}
264+
189265
- (void)testLocalizedFallbackTitle {
190266
FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init];
191267
id mockAuthContext = OCMClassMock([LAContext class]);
@@ -203,7 +279,7 @@ - (void)testLocalizedFallbackTitle {
203279
void (^reply)(BOOL, NSError *);
204280
[invocation getArgument:&reply atIndex:4];
205281
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
206-
reply(NO, [NSError errorWithDomain:@"error" code:99 userInfo:nil]);
282+
reply(YES, nil);
207283
});
208284
};
209285
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:reason reply:[OCMArg any]])
@@ -220,10 +296,7 @@ - (void)testLocalizedFallbackTitle {
220296
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
221297
[plugin handleMethodCall:call
222298
result:^(id _Nullable result) {
223-
XCTAssertTrue([NSThread isMainThread]);
224-
XCTAssertTrue([result isKindOfClass:[NSNumber class]]);
225299
OCMVerify([mockAuthContext setLocalizedFallbackTitle:localizedFallbackTitle]);
226-
XCTAssertFalse([result boolValue]);
227300
[expectation fulfill];
228301
}];
229302
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
@@ -245,7 +318,7 @@ - (void)testSkippedLocalizedFallbackTitle {
245318
void (^reply)(BOOL, NSError *);
246319
[invocation getArgument:&reply atIndex:4];
247320
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
248-
reply(NO, [NSError errorWithDomain:@"error" code:99 userInfo:nil]);
321+
reply(YES, nil);
249322
});
250323
};
251324
OCMStub([mockAuthContext evaluatePolicy:policy localizedReason:reason reply:[OCMArg any]])
@@ -260,10 +333,7 @@ - (void)testSkippedLocalizedFallbackTitle {
260333
XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"];
261334
[plugin handleMethodCall:call
262335
result:^(id _Nullable result) {
263-
XCTAssertTrue([NSThread isMainThread]);
264-
XCTAssertTrue([result isKindOfClass:[NSNumber class]]);
265336
OCMVerify([mockAuthContext setLocalizedFallbackTitle:nil]);
266-
XCTAssertFalse([result boolValue]);
267337
[expectation fulfill];
268338
}];
269339
[self waitForExpectationsWithTimeout:kTimeout handler:nil];

packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,26 +216,29 @@ - (void)handleAuthReplyWithSuccess:(BOOL)success
216216
result(@YES);
217217
} else {
218218
switch (error.code) {
219-
case LAErrorPasscodeNotSet:
220219
#pragma clang diagnostic push
221220
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
222-
// TODO(stuartmorgan): Remove the pragma and s/TouchID/Biometry/ in these constants when
223-
// iOS 10 support is dropped. The values are the same, only the names have changed.
221+
// TODO(stuartmorgan): Remove the pragma and s/TouchID/Biometry/ in these constants when
222+
// iOS 10 support is dropped. The values are the same, only the names have changed.
224223
case LAErrorTouchIDNotAvailable:
225224
case LAErrorTouchIDNotEnrolled:
226225
case LAErrorTouchIDLockout:
227226
#pragma clang diagnostic pop
228227
case LAErrorUserFallback:
228+
case LAErrorPasscodeNotSet:
229+
case LAErrorAuthenticationFailed:
229230
[self handleErrors:error flutterArguments:arguments withFlutterResult:result];
230231
return;
231232
case LAErrorSystemCancel:
232233
if ([arguments[@"stickyAuth"] boolValue]) {
233234
self->_lastCallArgs = arguments;
234235
self->_lastResult = result;
235-
return;
236+
} else {
237+
result(@NO);
236238
}
239+
return;
237240
}
238-
result(@NO);
241+
[self handleErrors:error flutterArguments:arguments withFlutterResult:result];
239242
}
240243
}
241244

packages/local_auth/local_auth_ios/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: local_auth_ios
22
description: iOS implementation of the local_auth plugin.
33
repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_ios
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22
5-
version: 1.0.10
5+
version: 1.0.11
66

77
environment:
88
sdk: ">=2.14.0 <3.0.0"

0 commit comments

Comments
 (0)