From e780cb8e79a57bfacccac41dd565b0ece89b68fa Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 1 Nov 2023 10:30:56 -0700 Subject: [PATCH] [local_auth] Update iOS to Pigeon 13 (#5269) Picks up several breaking changes, including the switch to unwrapped BOOL properties. --- .../local_auth/local_auth_ios/CHANGELOG.md | 4 + .../ios/RunnerTests/FLTLocalAuthPluginTests.m | 48 +++++------ .../ios/Classes/FLTLocalAuthPlugin.m | 11 ++- .../local_auth_ios/ios/Classes/messages.g.h | 28 +++++-- .../local_auth_ios/ios/Classes/messages.g.m | 81 ++++++++++++------- .../local_auth_ios/lib/src/messages.g.dart | 24 ++++-- .../local_auth/local_auth_ios/pubspec.yaml | 4 +- 7 files changed, 124 insertions(+), 76 deletions(-) diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index e6575246262c..b04c99869fa0 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.1.5 + +* Updates to Pigeon 13. + ## 1.1.4 * Adds pub topics to package metadata. diff --git a/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m index dff4c4c11ad1..a2c969055cef 100644 --- a/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m +++ b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m @@ -73,9 +73,9 @@ - (void)testSuccessfullAuthWithBiometrics { .andDo(backgroundThreadReplyCaller); XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; - [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:@YES - sticky:@NO - useErrorDialogs:@NO] + [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:YES + sticky:NO + useErrorDialogs:NO] strings:strings completion:^(FLAAuthResultDetails *_Nullable resultDetails, FlutterError *_Nullable error) { @@ -111,9 +111,9 @@ - (void)testSuccessfullAuthWithoutBiometrics { .andDo(backgroundThreadReplyCaller); XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; - [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:@NO - sticky:@NO - useErrorDialogs:@NO] + [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO + sticky:NO + useErrorDialogs:NO] strings:strings completion:^(FLAAuthResultDetails *_Nullable resultDetails, FlutterError *_Nullable error) { @@ -149,9 +149,9 @@ - (void)testFailedAuthWithBiometrics { .andDo(backgroundThreadReplyCaller); XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; - [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:@YES - sticky:@NO - useErrorDialogs:@NO] + [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:YES + sticky:NO + useErrorDialogs:NO] strings:strings completion:^(FLAAuthResultDetails *_Nullable resultDetails, FlutterError *_Nullable error) { @@ -191,9 +191,9 @@ - (void)testFailedWithUnknownErrorCode { .andDo(backgroundThreadReplyCaller); XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; - [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:@NO - sticky:@NO - useErrorDialogs:@NO] + [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO + sticky:NO + useErrorDialogs:NO] strings:strings completion:^(FLAAuthResultDetails *_Nullable resultDetails, FlutterError *_Nullable error) { @@ -229,9 +229,9 @@ - (void)testSystemCancelledWithoutStickyAuth { .andDo(backgroundThreadReplyCaller); XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; - [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:@NO - sticky:@NO - useErrorDialogs:@NO] + [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO + sticky:NO + useErrorDialogs:NO] strings:strings completion:^(FLAAuthResultDetails *_Nullable resultDetails, FlutterError *_Nullable error) { @@ -267,9 +267,9 @@ - (void)testFailedAuthWithoutBiometrics { .andDo(backgroundThreadReplyCaller); XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; - [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:@NO - sticky:@NO - useErrorDialogs:@NO] + [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO + sticky:NO + useErrorDialogs:NO] strings:strings completion:^(FLAAuthResultDetails *_Nullable resultDetails, FlutterError *_Nullable error) { @@ -310,9 +310,9 @@ - (void)testLocalizedFallbackTitle { .andDo(backgroundThreadReplyCaller); XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; - [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:@NO - sticky:@NO - useErrorDialogs:@NO] + [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO + sticky:NO + useErrorDialogs:NO] strings:strings completion:^(FLAAuthResultDetails *_Nullable resultDetails, FlutterError *_Nullable error) { @@ -348,9 +348,9 @@ - (void)testSkippedLocalizedFallbackTitle { .andDo(backgroundThreadReplyCaller); XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; - [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:@NO - sticky:@NO - useErrorDialogs:@NO] + [plugin authenticateWithOptions:[FLAAuthOptions makeWithBiometricOnly:NO + sticky:NO + useErrorDialogs:NO] strings:strings completion:^(FLAAuthResultDetails *_Nullable resultDetails, FlutterError *_Nullable error) { diff --git a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m index ea105f3943ce..4962c39c396a 100644 --- a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m +++ b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m @@ -60,7 +60,7 @@ @implementation FLTLocalAuthPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FLTLocalAuthPlugin *instance = [[FLTLocalAuthPlugin alloc] init]; [registrar addApplicationDelegate:instance]; - FLALocalAuthApiSetup([registrar messenger], instance); + SetUpFLALocalAuthApi([registrar messenger], instance); } - (instancetype)init { @@ -86,9 +86,8 @@ - (void)authenticateWithOptions:(nonnull FLAAuthOptions *)options self.lastCallState = nil; context.localizedFallbackTitle = strings.localizedFallbackTitle; - LAPolicy policy = options.biometricOnly.boolValue - ? LAPolicyDeviceOwnerAuthenticationWithBiometrics - : LAPolicyDeviceOwnerAuthentication; + LAPolicy policy = options.biometricOnly ? LAPolicyDeviceOwnerAuthenticationWithBiometrics + : LAPolicyDeviceOwnerAuthentication; if ([context canEvaluatePolicy:policy error:&authError]) { [context evaluatePolicy:policy localizedReason:strings.reason @@ -208,7 +207,7 @@ - (void)handleAuthReplyWithSuccess:(BOOL)success [self handleError:error withOptions:options strings:strings completion:completion]; return; case LAErrorSystemCancel: - if ([options.sticky boolValue]) { + if (options.sticky) { _lastCallState = [[FLAStickyAuthState alloc] initWithOptions:options strings:strings resultHandler:completion]; @@ -237,7 +236,7 @@ - (void)handleError:(NSError *)authError switch (authError.code) { case LAErrorPasscodeNotSet: case LAErrorBiometryNotEnrolled: - if (options.useErrorDialogs.boolValue) { + if (options.useErrorDialogs) { [self showAlertWithMessage:strings.goToSettingsDescription dismissButtonTitle:strings.cancelButton openSettingsButtonTitle:strings.goToSettingsButton diff --git a/packages/local_auth/local_auth_ios/ios/Classes/messages.g.h b/packages/local_auth/local_auth_ios/ios/Classes/messages.g.h index eda93cd93e6b..f8574c094fc6 100644 --- a/packages/local_auth/local_auth_ios/ios/Classes/messages.g.h +++ b/packages/local_auth/local_auth_ios/ios/Classes/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v9.2.5), do not edit directly. +// Autogenerated from Pigeon (v13.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @@ -27,12 +27,24 @@ typedef NS_ENUM(NSUInteger, FLAAuthResult) { FLAAuthResultErrorPasscodeNotSet = 4, }; +/// Wrapper for FLAAuthResult to allow for nullability. +@interface FLAAuthResultBox : NSObject +@property(nonatomic, assign) FLAAuthResult value; +- (instancetype)initWithValue:(FLAAuthResult)value; +@end + /// Pigeon equivalent of the subset of BiometricType used by iOS. typedef NS_ENUM(NSUInteger, FLAAuthBiometric) { FLAAuthBiometricFace = 0, FLAAuthBiometricFingerprint = 1, }; +/// Wrapper for FLAAuthBiometric to allow for nullability. +@interface FLAAuthBiometricBox : NSObject +@property(nonatomic, assign) FLAAuthBiometric value; +- (instancetype)initWithValue:(FLAAuthBiometric)value; +@end + @class FLAAuthStrings; @class FLAAuthOptions; @class FLAAuthResultDetails; @@ -61,12 +73,12 @@ typedef NS_ENUM(NSUInteger, FLAAuthBiometric) { @interface FLAAuthOptions : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithBiometricOnly:(NSNumber *)biometricOnly - sticky:(NSNumber *)sticky - useErrorDialogs:(NSNumber *)useErrorDialogs; -@property(nonatomic, strong) NSNumber *biometricOnly; -@property(nonatomic, strong) NSNumber *sticky; -@property(nonatomic, strong) NSNumber *useErrorDialogs; ++ (instancetype)makeWithBiometricOnly:(BOOL)biometricOnly + sticky:(BOOL)sticky + useErrorDialogs:(BOOL)useErrorDialogs; +@property(nonatomic, assign) BOOL biometricOnly; +@property(nonatomic, assign) BOOL sticky; +@property(nonatomic, assign) BOOL useErrorDialogs; @end @interface FLAAuthResultDetails : NSObject @@ -117,7 +129,7 @@ NSObject *FLALocalAuthApiGetCodec(void); FlutterError *_Nullable))completion; @end -extern void FLALocalAuthApiSetup(id binaryMessenger, +extern void SetUpFLALocalAuthApi(id binaryMessenger, NSObject *_Nullable api); NS_ASSUME_NONNULL_END diff --git a/packages/local_auth/local_auth_ios/ios/Classes/messages.g.m b/packages/local_auth/local_auth_ios/ios/Classes/messages.g.m index 550e7ada9e2f..5e58f7e1e398 100644 --- a/packages/local_auth/local_auth_ios/ios/Classes/messages.g.m +++ b/packages/local_auth/local_auth_ios/ios/Classes/messages.g.m @@ -1,16 +1,43 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v9.2.5), do not edit directly. +// Autogenerated from Pigeon (v13.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "messages.g.h" + +#if TARGET_OS_OSX +#import +#else #import +#endif #if !__has_feature(objc_arc) #error File requires ARC to be enabled. #endif +/// Possible outcomes of an authentication attempt. +@implementation FLAAuthResultBox +- (instancetype)initWithValue:(FLAAuthResult)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + +/// Pigeon equivalent of the subset of BiometricType used by iOS. +@implementation FLAAuthBiometricBox +- (instancetype)initWithValue:(FLAAuthBiometric)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + static NSArray *wrapResult(id result, FlutterError *error) { if (error) { return @[ @@ -67,15 +94,10 @@ + (instancetype)makeWithReason:(NSString *)reason + (FLAAuthStrings *)fromList:(NSArray *)list { FLAAuthStrings *pigeonResult = [[FLAAuthStrings alloc] init]; pigeonResult.reason = GetNullableObjectAtIndex(list, 0); - NSAssert(pigeonResult.reason != nil, @""); pigeonResult.lockOut = GetNullableObjectAtIndex(list, 1); - NSAssert(pigeonResult.lockOut != nil, @""); pigeonResult.goToSettingsButton = GetNullableObjectAtIndex(list, 2); - NSAssert(pigeonResult.goToSettingsButton != nil, @""); pigeonResult.goToSettingsDescription = GetNullableObjectAtIndex(list, 3); - NSAssert(pigeonResult.goToSettingsDescription != nil, @""); pigeonResult.cancelButton = GetNullableObjectAtIndex(list, 4); - NSAssert(pigeonResult.cancelButton != nil, @""); pigeonResult.localizedFallbackTitle = GetNullableObjectAtIndex(list, 5); return pigeonResult; } @@ -84,20 +106,20 @@ + (nullable FLAAuthStrings *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - (self.reason ?: [NSNull null]), - (self.lockOut ?: [NSNull null]), - (self.goToSettingsButton ?: [NSNull null]), - (self.goToSettingsDescription ?: [NSNull null]), - (self.cancelButton ?: [NSNull null]), - (self.localizedFallbackTitle ?: [NSNull null]), + self.reason ?: [NSNull null], + self.lockOut ?: [NSNull null], + self.goToSettingsButton ?: [NSNull null], + self.goToSettingsDescription ?: [NSNull null], + self.cancelButton ?: [NSNull null], + self.localizedFallbackTitle ?: [NSNull null], ]; } @end @implementation FLAAuthOptions -+ (instancetype)makeWithBiometricOnly:(NSNumber *)biometricOnly - sticky:(NSNumber *)sticky - useErrorDialogs:(NSNumber *)useErrorDialogs { ++ (instancetype)makeWithBiometricOnly:(BOOL)biometricOnly + sticky:(BOOL)sticky + useErrorDialogs:(BOOL)useErrorDialogs { FLAAuthOptions *pigeonResult = [[FLAAuthOptions alloc] init]; pigeonResult.biometricOnly = biometricOnly; pigeonResult.sticky = sticky; @@ -106,12 +128,9 @@ + (instancetype)makeWithBiometricOnly:(NSNumber *)biometricOnly } + (FLAAuthOptions *)fromList:(NSArray *)list { FLAAuthOptions *pigeonResult = [[FLAAuthOptions alloc] init]; - pigeonResult.biometricOnly = GetNullableObjectAtIndex(list, 0); - NSAssert(pigeonResult.biometricOnly != nil, @""); - pigeonResult.sticky = GetNullableObjectAtIndex(list, 1); - NSAssert(pigeonResult.sticky != nil, @""); - pigeonResult.useErrorDialogs = GetNullableObjectAtIndex(list, 2); - NSAssert(pigeonResult.useErrorDialogs != nil, @""); + pigeonResult.biometricOnly = [GetNullableObjectAtIndex(list, 0) boolValue]; + pigeonResult.sticky = [GetNullableObjectAtIndex(list, 1) boolValue]; + pigeonResult.useErrorDialogs = [GetNullableObjectAtIndex(list, 2) boolValue]; return pigeonResult; } + (nullable FLAAuthOptions *)nullableFromList:(NSArray *)list { @@ -119,9 +138,9 @@ + (nullable FLAAuthOptions *)nullableFromList:(NSArray *)list { } - (NSArray *)toList { return @[ - (self.biometricOnly ?: [NSNull null]), - (self.sticky ?: [NSNull null]), - (self.useErrorDialogs ?: [NSNull null]), + @(self.biometricOnly), + @(self.sticky), + @(self.useErrorDialogs), ]; } @end @@ -149,8 +168,8 @@ + (nullable FLAAuthResultDetails *)nullableFromList:(NSArray *)list { - (NSArray *)toList { return @[ @(self.result), - (self.errorMessage ?: [NSNull null]), - (self.errorDetails ?: [NSNull null]), + self.errorMessage ?: [NSNull null], + self.errorDetails ?: [NSNull null], ]; } @end @@ -239,12 +258,12 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { return sSharedObject; } -void FLALocalAuthApiSetup(id binaryMessenger, +void SetUpFLALocalAuthApi(id binaryMessenger, NSObject *api) { /// Returns true if this device supports authentication. { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.LocalAuthApi.isDeviceSupported" + initWithName:@"dev.flutter.pigeon.local_auth_ios.LocalAuthApi.isDeviceSupported" binaryMessenger:binaryMessenger codec:FLALocalAuthApiGetCodec()]; if (api) { @@ -265,7 +284,7 @@ void FLALocalAuthApiSetup(id binaryMessenger, /// any biometrics are enrolled or not. { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.LocalAuthApi.deviceCanSupportBiometrics" + initWithName:@"dev.flutter.pigeon.local_auth_ios.LocalAuthApi.deviceCanSupportBiometrics" binaryMessenger:binaryMessenger codec:FLALocalAuthApiGetCodec()]; if (api) { @@ -286,7 +305,7 @@ void FLALocalAuthApiSetup(id binaryMessenger, /// without additional setup. { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.LocalAuthApi.getEnrolledBiometrics" + initWithName:@"dev.flutter.pigeon.local_auth_ios.LocalAuthApi.getEnrolledBiometrics" binaryMessenger:binaryMessenger codec:FLALocalAuthApiGetCodec()]; if (api) { @@ -307,7 +326,7 @@ void FLALocalAuthApiSetup(id binaryMessenger, /// [strings] for any UI. { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.LocalAuthApi.authenticate" + initWithName:@"dev.flutter.pigeon.local_auth_ios.LocalAuthApi.authenticate" binaryMessenger:binaryMessenger codec:FLALocalAuthApiGetCodec()]; if (api) { diff --git a/packages/local_auth/local_auth_ios/lib/src/messages.g.dart b/packages/local_auth/local_auth_ios/lib/src/messages.g.dart index 48ab4e53232f..cc93815a25ca 100644 --- a/packages/local_auth/local_auth_ios/lib/src/messages.g.dart +++ b/packages/local_auth/local_auth_ios/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v9.2.5), do not edit directly. +// Autogenerated from Pigeon (v13.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import @@ -11,6 +11,17 @@ import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + /// Possible outcomes of an authentication attempt. enum AuthResult { /// The user authenticated successfully. @@ -221,7 +232,8 @@ class LocalAuthApi { /// Returns true if this device supports authentication. Future isDeviceSupported() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.LocalAuthApi.isDeviceSupported', codec, + 'dev.flutter.pigeon.local_auth_ios.LocalAuthApi.isDeviceSupported', + codec, binaryMessenger: _binaryMessenger); final List? replyList = await channel.send(null) as List?; if (replyList == null) { @@ -249,7 +261,8 @@ class LocalAuthApi { /// any biometrics are enrolled or not. Future deviceCanSupportBiometrics() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.LocalAuthApi.deviceCanSupportBiometrics', codec, + 'dev.flutter.pigeon.local_auth_ios.LocalAuthApi.deviceCanSupportBiometrics', + codec, binaryMessenger: _binaryMessenger); final List? replyList = await channel.send(null) as List?; if (replyList == null) { @@ -277,7 +290,8 @@ class LocalAuthApi { /// without additional setup. Future> getEnrolledBiometrics() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.LocalAuthApi.getEnrolledBiometrics', codec, + 'dev.flutter.pigeon.local_auth_ios.LocalAuthApi.getEnrolledBiometrics', + codec, binaryMessenger: _binaryMessenger); final List? replyList = await channel.send(null) as List?; if (replyList == null) { @@ -306,7 +320,7 @@ class LocalAuthApi { Future authenticate( AuthOptions arg_options, AuthStrings arg_strings) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.LocalAuthApi.authenticate', codec, + 'dev.flutter.pigeon.local_auth_ios.LocalAuthApi.authenticate', codec, binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_options, arg_strings]) as List?; diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 2e336117eb60..a4fcff5b55d7 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/packages/tree/main/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.1.4 +version: 1.1.5 environment: sdk: ">=3.0.0 <4.0.0" @@ -27,7 +27,7 @@ dev_dependencies: flutter_test: sdk: flutter mockito: 5.4.1 - pigeon: ^9.2.4 + pigeon: ^13.0.0 topics: - authentication