【Flutter】katana_modelの様にシステムを簡単に切り替えることが可能な認証パッケージ

こんにちは。広瀬マサルです。

後々リリース予定の総合フレームワークのために利用されるパッケージをいくつか紹介します。

今回は認証周りのパッケージとなります。

Firebase Authenticationを後々利用するためのテスト用のパッケージとなります。

使い方をまとめたので興味ある方はぜひ使ってみてください！

# katana_auth

https://pub.dev/packages/katana_auth

# はじめに

認証周りを利用するためのFirebaseAuthenticationは便利です。

メールアドレス認証や電話番号での認証、SNSアカウントを用いた認証など様々な認証を簡単に実装することが出来ます。

ただ、後々にFirebaseの認証を利用するにしてもアプリのモックアップを作成する際にサーバーへ接続せずに認証機能を実装したい場合やテストコードで認証機能を実装したい場合があります。

そのため[katana_model](https://pub.dev/packages/katana_model)で実装したときと同じ様にアダプターを利用してFirebaseやローカルでの認証を切り替えることが可能になるようなパッケージを実装しました。

また、GoogleサインインやAppleサインインの利用の有無をアプリによって切り替え易いようにインターフェースを整備しました

# インストール

下記パッケージをインポートします。

```dart
flutter pub add katana_auth
```

Firestoreを利用する場合は下記のパッケージを合わせてインポートします。

```dart
flutter pub add katana_auth_firebase
```

# 実装

## 事前準備

必ず`AuthAdapterScope`のWidgetをアプリのルート近くに配置します。

adapterのパラメーターに`RuntimeAuthAdapter`といったAuthAdapterを渡します。

```dart
// main.dart
import 'package:flutter/material.dart';
import 'package:katana_auth/katana_auth.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return AuthAdapterScope(
      adapter: const RuntimeAuthAdapter(),
      child: MaterialApp(
        home: const AuthPage(),
        title: "Flutter Demo",
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
      ),
    );
  }
}
```

## 認証オブジェクトの作成

認証を行うにはまず`Authentication`を作成しどこかに保持しておきます。

`Authentication`のオブジェクトは下記のデータを取得することができるため認証の状態を確認可能です。

- isSignedIn：認証済みの場合`true`を返します。
- isAnonymously：匿名認証の場合`true`を返します。
- userId：ユーザーIDを返します。
- userEmail：メール認証等を行った場合ユーザーのメールアドレスを返します。
- userPhoneNumber：電話番号認証を行った場合ユーザーの電話番号を返します。

また、`Authentication`はChangeNotifierを継承しているので、`addListener`やriverpodの`ChangeNotifierProvider`を利用する等して更新を監視することが可能です。

```dart
// auth_page.dart
import 'package:flutter/material.dart';
import 'package:katana_auth/katana_auth.dart';

class AuthPage extends StatefulWidget {
  const AuthPage({super.key});

  @override
  State<StatefulWidget> createState() => AuthPageState();
}

class AuthPageState extends State<AuthPage> {
  final auth = Authentication();

  @override
  void initState() {
    super.initState();
    auth.addListener(_handledOnUpdate);
  }

  void _handledOnUpdate() {
    setState(() {});
  }

  @override
  void dispose() {
    super.dispose();
    auth.removeListener(_handledOnUpdate);
    auth.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("App Demo")),
      body: ListView(
        children: [
          ListTile(
            title: Text("SignedIn: ${auth.isSignedIn}"),
          ),
          ListTile(
            title: Text("Anonymously: ${auth.isAnonymously}"),
          ),
          ListTile(
            title: Text("ID: ${auth.userId}"),
          ),
          ListTile(
            title: Text("Email: ${auth.userEmail}"),
          ),
          ListTile(
            title: Text("Phone: ${auth.userPhoneNumber}"),
          ),
        ],
      ),
    );
  }
}
```

## ユーザー登録・ログイン

ユーザー登録を行うためには`auth.register`メソッドを利用します。

引数には`RegisterAuthProvider`を渡す必要があります。

RegisterAuthProviderは`register`メソッドがある`AuthQuery`を使用して取得してください。

（AuthQueryについては後述します）

```dart
await auth.register(
  EmailAndPasswordAuthQuery.register(
    email: "test@email.com",
    password: "12345678",
  ),
);
```

また、ログインを行うには`auth.signIn`を使用します。

引数にはSignInAuthProviderを渡す必要があり、`signIn`メソッドがある`AuthQuery`を使用して取得してください。

```dart
await auth.signIn(
  EmailAndPasswordAuthQuery.signIn(
    email: "test@email.com",
    password: "12345678",
  ),
);
```

メールリンク認証やSMS認証を行うといった、認証の確定を行う場合は`auth.confirmSignIn`を使用します。

```dart
await auth.confirmSignIn(
  SmsAuthQuery.confirmSignIn(
    code: "012345",
  ),
);
```

## ユーザー情報の変更

ユーザー情報を変更する場合は`auth.change`を使用します。

引数に`ChangeAuthProvider`を渡しますが、変更する内容によってAuthQueryのメソッドを変えることができます。

- `EmailAndPasswordAuthQuery.changeEmail`：メールアドレスの変更。
- `EmailAndPasswordAuthQuery.changePassword`：パスワードの変更。
- `SmsAuthQuery.changePhoneNumber`：電話番号の変更。

```dart
await auth.change(
  EmailAndPasswordAuthQuery.changeEmail(
    email: "changed@email.com"
  ),
);
```

※ログイン時にしか利用できません。

## ログアウト

ログアウトを行う場合は`auth.signOut`を使用します。

ログイン時にのみ利用可能で、引数は必要ありません。

```dart
await auth.signOut();
```

# AuthAdapter

`AuthAdapterScope`を定義する際に渡すことで認証システムを変更することが可能です。

- `RuntimeAuthAdapter`：アプリ起動時のみで動作する認証システム。アプリを起動し直すと認証情報はリセットされます。テスト時に利用してください。
- `LocalAuthAdapter`：端末ローカルのみで動作する認証システム。アプリを起動し直しても認証情報は残りますが他端末間で共有はできません。
- `FirebaseAuthAdapter`：FirebaseAuthenticationのシステム。端末間で認証情報を共有できます。Firebaseの[初期設定](https://firebase.google.com/docs/auth/flutter/start)が必要です。

# AuthQuery

認証プロバイダ別に`AuthQuery`が用意されています。

`AuthQuery`のメソッドを利用することにより`Authentication`クラスで用意されている認証用の機能を利用することが可能です。

（認証プロバイダによって利用できる機能が限定されます）

- `AnonymouslyAuthQuery`：匿名認証を提供するプロバイダ用AuthQuery。
- `EmailAndPasswordAuthQuery`：メールアドレスとパスワードでの認証を提供するプロバイダ用AuthQuery。
- `EmailLinkAuthQuery`：メールリンクでの認証を提供するプロバイダ用AuthQuery。
- `SmsAuthQuery`：SMSでの認証を提供するプロバイダ用AuthQuery。
- `SnsSignInAuthProvider`：SNSアカウントでの認証を提供するプロバイダ用AuthQueryの抽象クラス。抽象クラスなので他の関連パッケージを読み込むことによって実際のAuthQueryを利用することが可能。

# おわりに

自分で使う用途で作ったものですが実装の思想的に合ってそうならぜひぜひ使ってみてください！

また、[こちら](https://github.com/mathrunet/flutter_masamune)にソースを公開しているのでissueやPullRequestをお待ちしてます！

また仕事の依頼等ございましたら、私の[Twitter](https://twitter.com/mathru)や[Webサイト](https://mathru.net)で直接ご連絡をお願いいたします！

https://mathru.net/ja/contact

# GitHub Sponsors

スポンサーを随時募集してます。ご支援お待ちしております！

https://github.com/sponsors/mathrunet