📥

FlutterWebでFirebaseのメールリンク認証を使う

2024/03/28に公開
修正リクは ➡️ ywake/zenn

前回、FlutterでFirebaseのメールリンク認証を使うでiOSとAndroidでの方法を紹介しましたが、今回はfirebase_ui_authで現状サポートされていないFlutterWebでの方法の紹介です。

前回の続きを想定しているので基本的な設定は省略します。詳しくは前回の記事を参照してください。

これからやること

  1. WebはDynamicLinkをそのまま使えませんので、EmailLinkのリダイレクト先をサービスのログイン処理用のURLに設定します。
  2. 入力されたメールアドレスを一時的に保存します。
  3. 1のURLを処理できるようにgo_router等を設定します。
  4. URLを受け取った後のログイン処理を行います。

1. EmailLinkのリダイレクト先を設定する

前回の🎯Flutterの実装で設定したActionCodeSettingsのurlを変更します。

 final origin = kDebugMode
   ? 'http://localhost:8081' // httpにしてください
   : 'https://your-domain.com';
 ActionCodeSettings(
-  url: 'https://xxx.page.link',
+  url: '$origin/login/finish', // パスは自由です
   dynamicLinkDomain: 'xxx.page.link',
   handleCodeInApp: true,
   // ...
 );

一応デバッグ時はlocalhostを使うようにしています。ポートは好きにしてください。
ただし、flutter runする時、デフォルトではポートがランダムなので--web-port 8081等で指定する必要があります。

2. 入力されたメールアドレスを一時的に保存する

  • 保存にはshared_preferencesを使います。(shared_preferencesの使い方は本稿では省略します)
  • 入力されたメールアドレスを受け取る必要があります。
    • 今回は簡単にfirebase_ui_authEmailLinkSignInScreenを弄って使います。
    • 自分で実装するのももちろんありです。

私のGitHubリポジトリにある改造したfirebase_ui_authをインストールします。
(保守し続けていない可能性が高いので、その時はdiffを参照してご自身のフォークに反映して使ってください)

変更内容は
・webのサポートを追加
・EmailLinkSignInScreenにonLinkSentコールバックを追加
の2点のみです。

pubspec.yaml

dependencies:
  firebase_ui_auth:
    git:
      url: https://github.com/ywake/FirebaseUI-Flutter.git
      path: packages/firebase_ui_auth
      ref: email-link-web

dependency_overrides:
  firebase_ui_auth:
    git:
      url: https://github.com/ywake/FirebaseUI-Flutter.git
      path: packages/firebase_ui_auth
      ref: email-link-web

これでEmailLinkSignInScreenonLinkSentコールバックが追加されましたので、それを使ってメールアドレスを保存します。

class EmailLinkView extends StatelessWidget {
  const EmailLinkView({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return EmailLinkSignInScreen(
      onLinkSent: (email) {
        sharedPreferences.setString('emailForSignIn', email);
      },
    );
  }
}

3. URLを処理できるようにする

今回はgo_routerを使ってURLを処理できるようにします。(これも基本的な解説は省略します)
(ちなみにバージョンは13.x.xを想定しています。APIの更新早いので変わっているかもしれません)

import 'package:go_router/go_router.dart';

GoRouter(
  routes: [
    // ...
    GoRoute(
      path: '/login/link',
      pageBuilder: (context, state) => const EmailLinkView(),
    ),
+   GoRoute(
+     path: '/login/finish',
+     pageBuilder: (context, state) => FinishLogin(uri: state.uri),
+   ),
  ],
);

これから定義するFinishLoginUriを渡しています。これがDynamicLinkから伝わる情報になります。

それとmain.dartに以下を追加します。

void main() {
+ setUrlStrategy(PathUrlStrategy());
  runApp(MyApp());
}

これがないデフォルトではURLがyour-domain.com/#/pathといった形になってしまいカッコ悪いです。
もちろん#があるままでもできますので、その場合はこれまでのコードを合わせてください。

4. ログイン処理を行う

ここまで設定すれば、送られてきたメールリンクをブラウザで開くとyour-domain.com/login/finishに飛ぶはずですので、あとはそのページでログイン処理を行います。

概要を示すための適当なコードなので、適宜修正してください。

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class FinishLogin extends StatelessWidget {
  const FinishLogin({Key? key, required this.uri}) : super(key: key);
  final Uri uri;

  
  Widget build(BuildContext context) {
    if (FirebaseAuth.instance.isSignInWithEmailLink(uri.toString())) {
      // ログイン処理
      FirebaseAuth.instance.signInWithEmailLink(
        email: sharedPreferences.getString('emailForSignIn')!,
        emailLink: uri.toString(),
      ).then((_) {
        // ログイン成功時の処理
        sharedPreferences.remove('emailForSignIn');
      }).catchError((e) {
        // ログイン失敗時の処理
      });
      return Center(
        child: CircularProgressIndicator(),
      );
    } else {
      // 省略
    }
  }
}

go_routerから持ってきた「URL」と、SharedPreferencesに保存した「入力されたメールアドレス」をFirebaseAuth.instance.signInWithEmailLink()に渡してログイン処理を行います。

5. 終わり🎉

お疲れ様でした!
いつかfirebase_ui_authでサポートされて簡単になることを願っています。


関連記事

Discussion