LoginSignup
14
2

【Firebase】SMS認証でアカウント乗っ取りを防げ!

Last updated at Posted at 2023-12-13

※この記事は、筑波大学情報科学類とのコラボプロジェクト筑波ミライラボ Advent Calendar 2023の12月14日の記事です。

こんにちは、1年の青柳世成です。
今回はFlutterを使ってSMS認証を作っていきたいと思います。
*今回はAndroid版しか作りません。

この記事のターゲット

・Flutterはある程度分かるけどFirebaseについての知識がない!
・Firebaseを何から始めればいいのかわからない🧐
という方向けにちょいムズイレベルのSMS認証から入っていただければと思い作りました!

SMS認証とは

SMS認証とは、スマホやガラケーなどの携帯電話宛に送信されたSMSに記載のある確認コードを入力してログインする仕組みです。

SMS認証を導入することで、セキュリティ対策を大幅に強化できます。基本的に本人以外がデバイスを所持していることは少ないため、悪意のある不正アクセスを防ぎやすい傾向にあります。
また、SMS認証は電話番号にメッセージを送信する方法であるため、第三者にパスワードが漏れるといった心配もありません。
端末取られたらどうしようもなくね?って思ってる人は🤫

用意するもの

Android Studio, FlutterSDK, Googleアカウント, Firebaseプロジェクト
(多分これで全部だと思うけど多すぎて忘れてるかも...)

Firebaseプロジェクトの作成

リンクを開いたら「コンソールへ移動」ボタンを押してください。
コンソールへ移動
このような画面に移るはずです。
「プロジェクトを追加」を押して、手順に沿ってプロジェクトを作成してください。
Firebase コンソール
Flutterを選択してください。
Flutter
必要なものを入れていきます。
次のセクションへGO!!
※Flutterを押したらその画面のままタブを置いといてください、この後使います。

前準備

Node.jsがインストールされている前提で話を進めていきます。
まだインストールしていない方はこちらから道なりにインストールしてください。

Firebase CLI

Terminalを開き、次のコマンドを実行してください。

npm install -g firebase-tools

このコマンドはFirebase CLIをインストールするコマンドです。
Googleアカウントにログインする必要があるので、次のコマンドでログインします。

firebase login

ログインできたら、Firebaseプロジェクトを一覧表示し、CLIが正しくインストールされていてアカウントにアクセスしていることをテストします。

firebase projects:list

先ほど作成したFirebaseプロジェクトが表示されていたらOKです!

FlutterFire

※この手順の前にAndroid Studioでいつも通りプロジェクトを作ってください。
Android Studioプロジェクトからターミナルを開き、以下のコマンドを入力してください。

dart pub global activate flutterfire_cli

実行後

Activated flutterfire_cli 0.2.7.

と表示されたら以下のボタンを押して二個目のコマンドをコピーします。
FlutterFire CLI
コピーしたコマンドをターミナルに貼り付けます。

Which platforms should your configuration support (use arrow keys & space to select)?

という表示が出たら、どのプラットフォームで作りたいか?ということなので、Androidだけを選びます。

Android以外のチェックを外します。矢印キーで上下、スペースキーでチェックを切り替えることができます。
Androidのみにしたら、Enterキーで確定します。

次に

The files android/build.gradle & android/app/build.gradle will be updated to apply Firebase configuration and gradle build plugins. Do you want to continue?

と表示されるので、「y」キーを押してEnterで次に進んでください。

Learn more about using this file and next steps from the documentation:
 > https://firebase.google.com/docs/flutter/setup

と表示されたら完了です!

Android用のデバッグキー

Android Studioでいつも通りプロジェクトを作成し、プロジェクトを開いてターミナルで下記のコマンドを実行してください。

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

※このコマンドがJavaのエラーで使えない方は以下の記事を参考にしてください。
https://zenn.dev/satokazur222/articles/66568417b291d8

実行すると、SHA1キーが生成されます。
出力の以下の部分をコピーします。

Certificate fingerprints:
         SHA1: ここ!

コピーできたら、Firebaseプロジェクトの設定から以下の部分にキーを追加します。
SHA 証明書フィンガープリント

これで、準備が完了しました。
依存関係のインストールをしていきます!

依存関係のインストール

Android Studioでプロジェクトを作成し、以下のコマンドをターミナルに打ち、依存関係をインストールします。

- flutter pub add firebase_core
- flutter pub add firebase_auth

Authencation設定

Firebaseプロジェクト内メニューからAuthencationを探し、「始める」を押すことで認証方法の選択画面に飛びますので、電話番号を選択してください。

全ての準備が完了しました!
やっとコード書ける...

実装

main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

このコード全体の流れは、Flutterフレームワークを初期化し、Firebaseを初期化し、最終的にアプリケーションを開始するというものです。

void main() async {

エントリーポイントであるmain関数が定義されています。この関数はプログラムの開始点です。

WidgetsFlutterBinding.ensureInitialized();

ensureInitializedは、Flutterフレームワークが初期化されたことを確認します。

await Firebase.initializeApp();

Firebase.initializeApp()は、Firebaseの初期化を行います。この呼び出しは非同期処理であり、awaitが使用されています。Firebaseを使用する前に、Firebaseを初期化する必要があります。

runApp(MyApp());

runApp関数は、Flutterアプリを開始します。引数にはMyApp()というウィジェットが渡されています。MyAppはアプリケーションのメインウィジェットです。

※ここから下は全て

final FirebaseAuth _auth = FirebaseAuth.instance;

を定義してください。

_authはFirebase Authenticationを使用するためのFirebaseAuthのインスタンスです。これは、Firebaseアプリ全体で共有されるため、finalキーワードで宣言しています。

アカウント登録

Future<User?> signUpWithPhoneNumber(String phoneNumber) async {
    try {
      await _auth.verifyPhoneNumber(
        phoneNumber: phoneNumber,
        verificationCompleted: (PhoneAuthCredential credential) async {
          // 認証が完了した場合
          UserCredential authResult = await _auth.signInWithCredential(credential);
          return authResult.user;
        },
        verificationFailed: (FirebaseAuthException e) {
          // 認証が失敗した場合
          print("認証失敗: ${e.message}");
        },
        codeSent: (String verificationId, int? resendToken) {
          // SMSコードが送信された場合
          // ここで認証コードの入力UIを表示するなど...
          print("コード送信済み。認証ID: $verificationId");
        },
        codeAutoRetrievalTimeout: (String verificationId) {
          // 認証がタイムアウトした場合
          print("コード自動取得のタイムアウト。認証ID: $verificationId");
        },
      );
    } catch (e) {
      print("電話番号認証中のエラー: $e");
      return null;
    }
  }

Future<User?> signUpWithPhoneNumber(String phoneNumber) async {

signUpWithPhoneNumber関数は、電話番号を使用したユーザー登録を行います。戻り値の型はFutureであり、非同期関数であることを示すasyncが付いています。

try {

関数の実際の処理が始まります。エラーが発生する可能性がある処理をtry-catchで囲みます。

await _auth.verifyPhoneNumber(

_authのverifyPhoneNumberを呼び出して、電話番号を認証します。これにより、ユーザーにSMSコードが送信され、電話番号の確認が行われます。

verificationCompleted: (PhoneAuthCredential credential) async {

認証が完了した場合の関数。認証が完了すると、渡されたcredentialを使用してユーザーをサインインします。

verificationFailed: (FirebaseAuthException e) {

認証が失敗した場合の関数。エラーメッセージがコンソールに表示されます。

codeSent: (String verificationId, int? resendToken) {

SMSコードが送信された場合の関数。認証コードの送信が成功したことをログに表示します。

codeAutoRetrievalTimeout: (String verificationId) {

認証が自動的にタイムアウトした場合の関数。タイムアウトが発生したことをログに表示します。

} catch (e) {

try内で発生した例外をキャッチし、エラーメッセージをログに表示します。

return null;

電話番号認証中にエラーが発生した場合、nullを返します。

ログイン

Future<User?> signInWithPhoneNumber(String verificationId, String smsCode) async {
    try {
      PhoneAuthCredential credential = PhoneAuthProvider.credential(
        verificationId: verificationId,
        smsCode: smsCode,
      );
      UserCredential authResult = await _auth.signInWithCredential(credential);
      return authResult.user;
    } catch (e) {
      print("電話番号サインイン中のエラー: $e");
      return null;
    }
  }

Future<User?> signInWithPhoneNumber(String verificationId, String smsCode) async {

signInWithPhoneNumber関数は、Firebase Authenticationを使用して電話番号でのサインインを行います。引数には認証ID(verificationId)とSMSコード(smsCode)が渡されます。戻り値の型はFutureであり、非同期関数であることを示すasyncが付いています。

try {

関数の処理が始まります。エラーが発生する可能性がある処理をtry-catchで囲みます。

PhoneAuthCredential credential = PhoneAuthProvider.credential(

PhoneAuthProvider.credentialメソッドを使用して、電話番号認証のための情報を生成します。これにはverificationId(認証ID)とsmsCode(SMSコード)が必要です。

UserCredential authResult = await _auth.signInWithCredential(credential);

生成された情報を使用して、_authを介してサインインを試行します。サインインが成功すると、UserCredentialオブジェクトが得られます。

return authResult.user;

サインインが成功した場合、UserCredentialからUserを取得し、それを戻り値として返します。

} catch (e) {

try内で発生した例外をキャッチし、エラーメッセージをログに表示します。

print("電話番号サインイン中のエラー: $e");

キャッチしたエラーをログに表示します。

return null;

サインイン中にエラーが発生した場合、nullを返します。

ログアウト

Future<void> signOut() async {
    await _auth.signOut();
  }

Future<void> signOut() async {

signOut関数は、非同期関数であり、引数も戻り値もないことを示すFuture型です。サインアウト処理を行います。

await _auth.signOut();

_authのsignOutを呼び出して、現在のユーザーをサインアウトします。この処理は非同期であるため、awaitを使用して待機します。

あとがき

今回の記事では、Flutterを使用してSMS認証を実装する手順を紹介しました。Firebaseを利用して、セキュリティを強化した認証機能を実装することができます。

初めてFirebaseを使用する方や、SMS認証に興味がある方にとって、ちょうどいい難易度の内容だったでしょうか。

また、記事内で使用したリンクやコマンドがうまく動作しない場合は、最新の情報を確認してください。技術は常に進化していますので、変更があるかもしれません。

FlutterやFirebaseを使った開発は、ユーザーエクスペリエンス向上に貢献する強力なツールです。今後もさまざまな機能やアプリケーションを開発していく中で、新たな発見や工夫をしていただければ嬉しいです。

お疲れ様でした!どうぞ楽しいFlutter開発をお過ごしください。

14
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
14
2