LoginSignup
32
2

Flutterアプリで生体認証を活用し、サーバサイドのユーザと紐付けたい

Last updated at Posted at 2023-12-02

この記事はLITALICO Engineers Advent Calendar 2023 カレンダー1 の 3日目の記事です

はじめに

  • Flutterで生体認証を使うパッケージとしては、local_authが有名
    • 記事もいっぱい書かれている
      • 参考になりました、ありがとうございます
  • ただ、local_authは生体認証で認証できた/できなかったまでしか提供してくれない
    • サーバサイドと連携するアプリで活用することを考えると、サーバサイド側で保持しているユーザ情報とどう紐づけるか考える必要がある
    • 調べてみたところ、そこまで書かれている記事がないなと思ったので、まとめてみようと思った

概要図

Flutter生体認証.drawio.png

注)今回のサンプルでは概要を説明するために、passwordをflutter_secure_storageに保持しているが、トークンやその他、資格情報を保持し、passowrdを直接保持しないなどを適宜検討してください

使用するパッケージ

  • local_auth
    • 生体認証の利用、これを使うと、スマホの指紋認証、FaceIDなどを使った認証ができる
  • flutter_secure_storage
    • アプリ側でのID/パスワードの保持用
  • firebase_auth
    • サーバサイドの認証サンプル用

コードサンプル

Flutter PJ全体の環境設定

pubspec.yaml
dependencies:
  local_auth: ^2.1.7
  flutter_secure_storage: ^9.0.0
  firebase_auth: ^4.14.0

iOS向けの設定

info.plist
<key>NSFaceIDUsageDescription</key>
<string>生体認証を使用する目的を記述</string>

Android向けの設定

MainActivity.kt
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant

class MainActivity: FlutterFragmentActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine)
    }
}
AndroidManifest.xml
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.app">
        <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
    <manifest>

ユーザ登録

必要なパッケージの読み込み

regist_user.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

ユーザ登録

regist_user.dart
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final _storage = new FlutterSecureStorage();
/** 省略 **/

Future<void> registUser(String email, String password) async {
    // まず、Firebaseでユーザ登録を行う
    try {
        UserCredential _userCredential = await _firebaseAuth.createUserWithEmailAndPassword(
            email: email,
            password: password,
        );
    } on FirebaseAuthException catch (e) {
        debugPrint('debug: cannot regist: $e');
        //ToDo:エラーハンドリング
        return
    }

    // 次に、Firebaseで作成したユーザをSecureStorageに保存する
    await _storage.write(key: 'email', value: email);
    await _storage.write(key: 'password', value: password);
}

生体認証

必要なパッケージの読み込み

authenticate_user.dart
import 'package:local_auth/local_auth.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

利用可能な生体認証の確認

authenticate_user_with_biometric.dart
LocalAuthentication _localAuth = LocalAuthentication();
/** 省略 **/

Future<List<BiometricType>> _getAvailableBiometricTypes() async {
    List<BiometricType> availableBiometricTypes;
    try {
        availableBiometricTypes = await _localAuth.getAvailableBiometrics();
    } on PlatformException catch (e) {
        debugPrint('debug: cannot get availavle biometrics: $e');
    }
    return availableBiometricTypes;
}

生体認証を実地する

authenticate_user_with_biometric.dart
LocalAuthentication _localAuth = LocalAuthentication();
/** 省略 **/

Future<bool> _authenticateWithBiometric() async {
    bool authenticate = false;

    List<BiometricType> availableBiometricTypes = await _getAvailableBiometricTypes();

    try {
      if (availableBiometricTypes.contains(BiometricType.face)
          || availableBiometricTypes.contains(BiometricType.fingerprint)) {
        authenticate = await _localAuth.authenticateWithBiometrics(localizedReason: "認証してください");
      }
    } on PlatformException catch (e) {
        debugPrint('debug: cannot authenticate with biometrics: $e');
    }
    return authenticate;
  }

生体認証を通過後、保持していたID/パスワードで認証する

authenticate_user_with_biometric.dart
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final _storage = new FlutterSecureStorage();
/** 省略 **/

Future<void> _loginWithBiometric() async {
    bool authenticate = await _authenticateWithBiometric();

    if (authenticate) {
        String email = await _storage.read(key: 'email');
        String password = await _storage.read(key: 'password');

        try {
            final credential = await _firebaseAuth.signInWithEmailAndPassword(
                email: email,
                password: password
              );
        } on FirebaseAuthException catch (e) {
            debugPrint('debug: cannot login: $e');
        }

        // ToDo: ログイン後処理
    }
}

あとがき

配布するアプリで考えると、他にも下記のように色々やらないといけないことがあります

  • View(ユーザ登録画面、ログイン画面)の作成
  • エラーハンドリング
  • 認証情報の保持方法(JWTやセッションなど)

ただ、大まかな流れは確認いただけるかなと思います

簡単な動作検証しかしておりませんが、2時間くらいでやりたかったことが概ね確認できるのは、Flutter本当に良いなと思います

明日は、@tjinjinさんの記事です!おたのしみに〜!

32
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
2