iOSアプリにTouch IDを埋め込む





エントリー



iOS 8以降、AppleはサードパーティアプリケーションでTouch IDテクノロジー(iPhone 5sに組み込まれた指紋スキャナーを使用した認証)を使用する可能性へのアクセスを開きます。 この点で、開発者が正確に利用できるようになったもの、アプリケーションに埋め込む方法、動作を詳しく説明し、最も実装しやすい便利な「ラッパー」を共有したいと思います。 Touch IDのユースケースと思われます。



必要なAPIは、新しいLocalAuthenticationフレームワークに導入されています。 現時点では、その機能は指紋スキャナーとの対話に限定されていますが、より一般的な名前で判断すると、その機能セットは将来拡張される可能性があります。 フレームワークはユーザーに関するデータを提供しません(これは一般に論理的です)が、ユーザーに生体認証を使用した認証のみを提供できます(現時点では組み込みの指紋スキャナーです;具体的には、フレームワーク内のスキャナーについての話はありません、より一般的なものが使用されます) word Biometrics)。 出力で、ステータスを取得します。認証が成功したか、何かがおかしかったです。 実際、ほぼいつでも、デバイスを使用する人が所有者であるかどうかを判断できます。



これは、重要な操作を実行するときに、追加の保護としてTouch IDを使用することをお勧めします。 たとえば、資金の送金を確認するとき、重要な設定を変更するとき、セキュアチャットを初期化するときなど、スマートフォンが攻撃者の手に渡っていないことをアプリケーションができる限り確認する必要があります。



投稿を読みやすくするだけでなく再利用できるようにするために、Touch IDとの統合を、上記のシナリオを実装する「ラッパー」の形で説明することにしました。これにより、今後数時間の作業時間を節約できます。 説明は「タスクソリューション」の形式で示されているため、何が行われているのか、またその理由は明らかです。 それでは、始めましょう。



挑戦する



アプリケーションで重要な操作を実行する場合、組み込みの生体認証を使用してユーザー認証を要求できる必要があります。 このような認証を要求する必要性は、ユーザーがカスタマイズできる必要があります。 また、アプリケーションは、以前のバージョンのオペレーティングシステムと、生体認証を持たないデバイスで実行できることにも留意してください。



解決策



ソリューションは、BiometricAuthenticationFacadeクラスで提示されます。

まず最初に、最も重要なこと-LocalAuthenticationフレームワークとの相互作用を検討してください。 この部分はユーザーには表示されず、クラスインターフェイスからはアクセスできません。



クラス拡張で、コンテキストを保存するプロパティを宣言します。

@interface BiometricAuthenticationFacade () @property (nonatomic, strong) LAContext *authenticationContext; @end
      
      





APIの可用性を考慮して、プロパティを初期化します。

 - (instancetype)init { self = [super init]; if (self) { if (self.isIOS8AndLater) { self.authenticationContext = [[LAContext alloc] init]; } } return self; }
      
      





次に、ローカル認証の使用の可用性を返すメソッドを定義します。

 - (BOOL)isPassByBiometricsAvailable { return [self.authenticationContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:NULL]; }
      
      





パラメータとして、 canEvaluatePolicy:error:



メソッドはローカル認証のタイプを受け入れます。 現在、 LAPolicyDeviceOwnerAuthenticationWithBiometrics



1つのタイプのみLAPolicyDeviceOwnerAuthenticationWithBiometrics



おり、それはそれ自身を表しています。 デバイスがこの機能を物理的にサポートしていない場合、またはユーザーがスマートフォンの設定でこの機能を有効にしていない場合、生体認証の使用は利用できない場合があります。



ユーザー指紋スキャンを実行する要求は、次のとおりです。

 - (void)passByBiometricsWithReason:(NSString *)reason succesBlock:(void(^)())successBlock failureBlock:(void(^)(NSError *error))failureBlock { [self.authenticationContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:reason reply:^(BOOL success, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ if (success) { successBlock(); } else { failureBlock(error); } }); }]; }
      
      





パラメーターとして、 evaluatePolicy:localizedReason:reply:



メソッドは、上記のタイプのローカル認証、要求の理由を簡潔に説明するメッセージ、および手順全体の完了後に非同期に実行されるブロックを受け入れます。



メインスレッドでのreply



ブロックの実行は保証されていないことに注意してください(実際、メインスレッドでは呼び出されません)。そのため、 dispatch_async



呼び出しが追加されています。 そのままにしておくこともできますが、ほとんどの開発者は、メインスレッドで呼び出されるメソッドに渡されるブロックがメインスレッドでも呼び出されると想定し、追加のチェックは行いません。 それはとても歴史的に起こりました。



上記のメソッドを呼び出すと、ダイアログが表示されます。



  1. タイトルにはアプリケーションの名前( CFBundleDisplayName



    )が使用されます。
  2. localizedReason



    パラメーターとして指定されたストリング。
  3. このフィールドではすべてがそれほど単純ではありません。 クリックすると、パスワードを入力するためのダイアログが表示されることはありませんが、代わりにエラーでreply



    ブロックが呼び出されます。 エラーコードは文書化されています:

    LAErrorUserFallback

    ユーザーがフォールバックボタン(パスワードの入力)をタップしたため、認証がキャンセルされました。

    つまり、それは考案されました。 ユーザーFlanker_4が説明したように、システムはアプリケーションに代替認証、つまりアプリケーションパスワードの入力を個別に実行するよう促します。 つまり、パスワードの入力と検証のためのダイアログとロジックの実装は開発者にあります。
  4. リクエストをキャンセルするボタン。 その結果、対応するLAErrorUserCancel



    エラーでreply



    ブロックが呼び出されます。


スキャンが成功すると、 reply



ブロックが呼び出され、肯定的な結果が返されます。

evaluatePolicy:localizedReason:reply:



メソッドが呼び出されるたびに、スキャンのダイアログが表示されるわけではないことに注意してください。 つまり、最後のスキャンの成功には寿命があります。 数分以内に認証を再試行すると、すぐにreply



ブロックが呼び出され、肯定的な結果が得られます。



間違った指を使用し、5回連続でスキャンしようとすると、スマートフォンの設定で指定されたパスワードを入力するように求められます。



明確にするために、パスワードを作成せずにスマートフォンの設定でスキャナーをオンにすることは不可能であることを明確にします。

ユーザーが正しいパスワードを入力すると、再び指紋スキャンが提供されます。 しかし、 simplixユーザーが指摘したように、デバイスのパスワードがわかっている場合は、グローバルデバイス設定でスキャナーを無効にできます。 実際、スキャンのためにダイアログを再表示しても意味がありません。



これでLocalAuthentication



との相互作用がLocalAuthentication



ます。

ファサードのインターフェースの実装に移りましょう。



認証の可用性を確認する方法。 結果は、APIとスキャナーの可用性によって決まります。

 - (BOOL)isAuthenticationAvailable { return self.isIOS8AndLater && self.isPassByBiometricsAvailable; }
      
      





特定の操作で認証が有効かどうかを判断する方法:

 - (BOOL)isAuthenticationEnabledForFeature:(NSString *)featureName { return self.isAuthenticationAvailable && [self loadIsAuthenticationEnabledForFeature:featureName]; }
      
      





操作の例としては、設定へのアクセス、金銭取引の実行などがあります。

包含ステータスはNSUserDefaultsに保存されます。 loadIsAuthenticationEnabledForFeature:



メソッドの実装をloadIsAuthenticationEnabledForFeature:



に示しloadIsAuthenticationEnabledForFeature:







特定の操作の認証を有効にする方法:

 - (void)enableAuthenticationForFeature:(NSString *)featureName succesBlock:(void(^)())successBlock failureBlock:(void(^)(NSError *error))failureBlock { if (self.isAuthenticationAvailable) { if ([self isAuthenticationEnabledForFeature:featureName]) { successBlock(); } else { [self saveIsAuthenticationEnabled:YES forFeature:featureName]; successBlock(); } } else { failureBlock(self.authenticationUnavailabilityError); } }
      
      





このメソッドは、アプリケーションのユーザーが追加の検証が必要な操作を独立して決定できるようにするために必要です。

イネーブル状態はNSUserDefaults



保存されNSUserDefaults



saveIsAuthenticationEnabled:forFeature



メソッドの実装をsaveIsAuthenticationEnabled:forFeature



に示しsaveIsAuthenticationEnabled:forFeature







特定の操作の認証無効化方法:

 - (void)disableAuthenticationForFeature:(NSString *)featureName withReason:(NSString *)reason succesBlock:(void(^)())successBlock failureBlock:(void(^)(NSError *error))failureBlock { if (self.isAuthenticationAvailable) { if ([self isAuthenticationEnabledForFeature:featureName]) { [self passByBiometricsWithReason:reason succesBlock:^{ [self saveIsAuthenticationEnabled:NO forFeature:featureName]; successBlock(); } failureBlock:failureBlock]; } else { successBlock(); } } else { failureBlock(self.authenticationUnavailabilityError); } }
      
      





ご覧のとおり、オフにするには、攻撃者ではなくスマートフォンの所有者と取引していることを確認する必要があります。



操作にアクセスするためのユーザー認証要求メソッド:

 - (void)authenticateForAccessToFeature:(NSString *)featureName withReason:(NSString *)reason succesBlock:(void(^)())successBlock failureBlock:(void(^)(NSError *error))failureBlock { if (self.isAuthenticationAvailable) { if ([self isAuthenticationEnabledForFeature:featureName]) { [self passByBiometricsWithReason:reason succesBlock:successBlock failureBlock:failureBlock]; } else { successBlock(); } } else { failureBlock(self.authenticationUnavailabilityError); } }
      
      





操作にアクセスするためのユーザー認証の必要性に関する情報を保存および取得するためのメソッド(クラスインターフェイスからは利用不可):

 - (void)saveIsAuthenticationEnabled:(BOOL)isAuthenticationEnabled forFeature:(NSString *)featureName { NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSMutableDictionary *featuresDictionary = nil; NSDictionary *currentDictionary = [userDefaults valueForKey:kFeaturesDictionaryKey]; if (currentDictionary == nil) { featuresDictionary = [NSMutableDictionary dictionary]; } else { featuresDictionary = [NSMutableDictionary dictionaryWithDictionary:currentDictionary]; } [featuresDictionary setValue:@(isAuthenticationEnabled) forKey:featureName]; [userDefaults setValue:featuresDictionary forKey:kFeaturesDictionaryKey]; [userDefaults synchronize]; } - (BOOL)loadIsAuthenticationEnabledForFeature:(NSString *)featureName { NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; NSDictionary *featuresDictionary = [userDefaults valueForKey:kFeaturesDictionaryKey]; return [[featuresDictionary valueForKey:featureName] boolValue]; }
      
      





NSUserDefaults



ストレージとして使用されます。 キーの名前が競合する可能性を減らすために、すべてのデータは別の辞書に保存されます。

これで、ファサードの主要な実装が完了しました。



終了



最後に、最後まで読み終えた人のために、iPhone 5sのスキャナーに関するいくつかの興味深い事実を紹介します。





興味深い事実のソース: iOSセキュリティ

GitHubで利用可能な完全なソースコード: BiometricAuthenticationFacade



All Articles