※この記事は、筑波大学情報科学類とのコラボプロジェクト筑波ミライラボ Advent Calendar 2023の12月14日の記事です。
こんにちは、1年の青柳世成です。
今回はFlutterを使ってSMS認証を作っていきたいと思います。
*今回はAndroid版しか作りません。
この記事のターゲット
・Flutterはある程度分かるけどFirebaseについての知識がない!
・Firebaseを何から始めればいいのかわからない🧐
という方向けにちょいムズイレベルのSMS認証から入っていただければと思い作りました!
SMS認証とは
SMS認証とは、スマホやガラケーなどの携帯電話宛に送信されたSMSに記載のある確認コードを入力してログインする仕組みです。
SMS認証を導入することで、セキュリティ対策を大幅に強化できます。基本的に本人以外がデバイスを所持していることは少ないため、悪意のある不正アクセスを防ぎやすい傾向にあります。
また、SMS認証は電話番号にメッセージを送信する方法であるため、第三者にパスワードが漏れるといった心配もありません。
端末取られたらどうしようもなくね?って思ってる人は🤫
用意するもの
Android Studio, FlutterSDK, Googleアカウント, Firebaseプロジェクト
(多分これで全部だと思うけど多すぎて忘れてるかも...)
Firebaseプロジェクトの作成
リンクを開いたら「コンソールへ移動」ボタンを押してください。
このような画面に移るはずです。
「プロジェクトを追加」を押して、手順に沿ってプロジェクトを作成してください。
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.
と表示されたら以下のボタンを押して二個目のコマンドをコピーします。
コピーしたコマンドをターミナルに貼り付けます。
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プロジェクトの設定から以下の部分にキーを追加します。
これで、準備が完了しました。
依存関係のインストールをしていきます!
依存関係のインストール
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開発をお過ごしください。