Firebase匿名ログインで「とりあえず使える」を実現する
アプリをインストールした直後に「まずアカウント作ってください」って言われると、だいたいの人はそこで離脱する。特にゲームや生産性アプリみたいな「まず触ってみたい」系のアプリでは致命的。
PlayTimeではボス協力機能(他のユーザーとリアルタイムで協力するモード)にFirestore1を使っていて、Firestoreへのアクセスには認証が必須。でもユーザーにアカウント作成を強制したくなかった。そこでFirebase Anonymous Authentication2を使った。
結論から言うと、Firebaseコンソールで1箇所チェックを入れて、Swift側で3行書くだけ。それだけでユーザーは登録なしでアプリの全機能を使い始められる。後からApple IDやGoogleアカウントに紐づけることもできる。
匿名認証とは何か
Firebase3の匿名認証は、ユーザーに何も入力させずに一時的なアカウント(匿名UID)を自動発行する仕組み。このUIDでFirestoreのセキュリティルールを通過できるので、「認証済みだけどユーザー情報はない」という状態を作れる。
主な用途。
- お試し利用: アカウント作成前にアプリの機能を体験させる
- 段階的オンボーディング: まず匿名で使わせて、価値を感じたらアカウント作成を促す
- バックエンド認証: Firestoreやリアルタイムデータベースのセキュリティルールを通すためだけの最小限の認証
PlayTimeの場合、ボス協力機能で部屋を作成したりダメージを送信したりする際にFirestoreにアクセスする。匿名認証で発行されたUIDがあれば、セキュリティルールのrequest.auth != nullを通過できる。
💡 匿名認証のUIDはデバイスごとに固定。アプリを再起動しても同じUIDが維持される。アプリを削除して再インストールすると新しいUIDが発行される。
Firebase Console側の設定
有効化は1箇所だけ
- Firebase Consoleにアクセス
- プロジェクトを選択(PlayTimeの場合:
playtime-69c21) - Authentication → Sign-in method → 匿名 → 有効にする
これだけ。
⚠️ ここを有効にしないとThis operation is restricted to administrators onlyエラーが出る。 エラーコード17098。コード側を何回見直しても解決しない。Console側の設定忘れが原因のことが多い。
Swiftコード実装
基本の匿名ログイン
最小限のコードはこれだけ。
import FirebaseAuth
Auth.auth().signInAnonymously { result, error in
if let error = error {
print("匿名ログイン失敗: \(error.localizedDescription)")
return
}
print("匿名ログイン成功: uid=\(result?.user.uid ?? "nil")")
}
PlayTimeでの実装パターン
実際のPlayTimeでは、ボスタブを開いたタイミングで認証状態をチェックし、未ログインなら匿名ログインを実行している。
// BossViewModel.swift
func ensureAuthenticated() {
// 既にログイン済みならスキップ
if let user = Auth.auth().currentUser {
Log.notice("ensureAuthenticated: status=already_signed_in uid=\(user.uid)", .boss)
return
}
// 二重呼び出し防止
guard !isAuthenticating else {
Log.notice("ensureAuthenticated: status=skipped reason=already_authenticating", .boss)
return
}
isAuthenticating = true
Log.notice("ensureAuthenticated: status=starting_anonymous_sign_in", .boss)
Auth.auth().signInAnonymously { [weak self] result, error in
self?.isAuthenticating = false
if let error = error {
Log.error("signInAnonymously: status=failed error=\(error.localizedDescription)", .boss)
return
}
Log.notice("signInAnonymously: status=succeeded uid=\(result?.user.uid ?? "nil")", .boss)
// Firestore操作を開始
self?.fetchMyRooms()
}
}
ポイント。
currentUserのnilチェック: Firebaseは前回のセッション情報を保持するので、再起動後もcurrentUserが非nilなら再ログイン不要isAuthenticatingフラグ: 画面遷移やライフサイクルイベントでensureAuthenticatedが複数回呼ばれることがある。二重呼び出しを防ぐ- OSLogで状態を記録: エージェントがログを見て「なぜFirestoreアクセスに失敗したか」を追跡できる
isAnonymousプロパティ
匿名ユーザーかどうかはisAnonymousで判定できる。
if let user = Auth.auth().currentUser {
if user.isAnonymous {
// 匿名ユーザー → アカウント作成を促すバナーを表示
showUpgradeBanner()
} else {
// 正規ユーザー(Apple/Google等で認証済み)
showFullProfile()
}
}
セキュリティルール(Firestore)
認証済みのみ許可
最もシンプルなルール。匿名ユーザーも正規ユーザーもrequest.authが非nullになるため、両方ともアクセス可能。
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /rooms/{roomId} {
allow read, write: if request.auth != null;
}
}
}
匿名と正規ユーザーの区別
匿名ユーザーには読み取りのみ許可し、書き込みは正規ユーザーに限定する場合。
match /rooms/{roomId} {
// 読み取り: 認証済みなら誰でも(匿名含む)
allow read: if request.auth != null;
// 書き込み: 匿名ユーザーは不可
allow write: if request.auth != null
&& request.auth.token.firebase.sign_in_provider != 'anonymous';
}
request.auth.token.firebase.sign_in_providerで認証プロバイダを判定できる。匿名なら'anonymous'、Apple IDなら'apple.com'、Googleなら'google.com'が入る。
📝 PlayTimeのボス機能では匿名ユーザーにも書き込みを許可している。理由は「まず遊んでもらう」ことを優先したから。セキュリティ要件に応じて使い分けること。
アカウントリンキング — 匿名から正規ユーザーへの昇格
匿名ユーザーが気に入ってくれたら、Apple IDやGoogleアカウントに紐づけて正規ユーザーに昇格させる。これが「アカウントリンキング」。
// Apple IDとリンキングする例
func linkWithApple(credential: AuthCredential) {
guard let user = Auth.auth().currentUser, user.isAnonymous else { return }
user.link(with: credential) { result, error in
if let error = error {
Log.error("linkWithApple: status=failed error=\(error.localizedDescription)", .auth)
return
}
Log.notice("linkWithApple: status=succeeded uid=\(result?.user.uid ?? "nil") isAnonymous=\(result?.user.isAnonymous ?? true)", .auth)
// リンキング後、isAnonymousはfalseになる
}
}
リンキングの利点。
- UIDが維持される: 匿名時に作成したFirestoreのデータはそのまま引き継がれる
- シームレスな移行: ユーザーから見ると「アカウント作成」だが、裏側ではUIDの所有権が匿名→Apple IDに移行するだけ
- データ消失なし: 匿名UIDに紐づいていたドキュメントはリンキング後も同じUIDでアクセスできる
⚡ リンキングに失敗するケース: 既にそのApple ID/Googleアカウントで別のFirebaseユーザーが存在する場合。error.code == AuthErrorCode.credentialAlreadyInUseで検出できる。この場合はマージ処理が必要。
トラブルシューティング
「This operation is restricted to administrators only」
原因: Firebase Consoleで匿名認証が有効になっていない。
解決: Firebase Console → Authentication → Sign-in method → 匿名 → 有効にする。
エラーコード: 17098(FIRAuthErrorCodeAdminRestrictedOperation)。コード側のバグではない。
二重呼び出しによる認証の重複
画面遷移やSwiftUIのライフサイクルでsignInAnonymouslyが複数回呼ばれると、毎回新しい匿名UIDが発行される可能性がある。
解決: isAuthenticatingフラグで排他制御。上記のPlayTimeコード例参照。
currentUserがnilなのにsignInAnonymouslyも失敗する
原因の可能性。
- ネットワーク未接続(機内モードなど)
- Firebase SDKの初期化が完了していない(
FirebaseApp.configure()が未呼び出し) - GoogleService-Info.plistがバンドルに含まれていない
実機での動作検証
シミュレータでは問題なく動くが実機で失敗する場合、Provisioning Profile4の設定が原因のことがある。Capabilities → Push Notifications や Keychain Sharing の設定が影響する場合がある。
OSLogでの認証ログ確認方法
先日掲載したOSLog + NSLog二重出力の記事で紹介した方式が、まさに認証デバッグで威力を発揮する。
シミュレータ
# AUTH関連のログだけ取得
xcrun simctl spawn booted log stream \
--predicate 'subsystem == "jp.po-miyasaka.PlayTime" AND category == "Auth"' \
--level debug
実機(idevicesyslog5)
# 認証関連のログだけフィルタ
idevicesyslog -p PlayTime | grep "miyashi_AUTH"
# ボス機能も含めて認証周辺を確認
idevicesyslog -p PlayTime | grep "miyashi_"
認証フローの出力例。
[miyashi_BOSS] ensureAuthenticated: status=starting_anonymous_sign_in
[miyashi_BOSS] signInAnonymously: status=succeeded uid=abc123def456
[miyashi_BOSS] fetchMyRooms: status=querying
エラー発生時。
[miyashi_BOSS] signInAnonymously: status=failed error=This operation is restricted to administrators only.
このログを見れば「Firebase Consoleで匿名認証が有効になっていない」と即座に判断できる。Notionに「ボスタブで認証エラーが出る」と書くだけで、エージェントがログを取得→原因特定→Console設定を確認→修正提案まで自動で進んでくれる。
まとめ
Firebase匿名認証の導入は3ステップ。
- Firebase Console → Authentication → 匿名を有効化
Auth.auth().signInAnonymously()を呼ぶ- Firestoreルールで
request.auth != nullを設定
これだけでユーザーはアカウント作成なしにアプリの全機能を使える。気に入ったらuser.link(with:)で正規アカウントに昇格。UIDもデータもそのまま引き継がれる。
自分の場合、PlayTimeのボス協力機能でこれを入れた。認証周りのトラブルはOSLogのmiyashi_AUTHフィルタで一発で追跡できるようになっているので、Notionに「認証エラー出てる」と書くだけであとはエージェントが勝手に解決してくれる。
Footnotes
-
Firestore — Firebase上のNoSQLドキュメントデータベース。リアルタイム同期とオフラインサポートが特徴。セキュリティルールで認証状態に基づくアクセス制御が可能。 ↩
-
Anonymous Authentication — Firebase認証の一機能。ユーザー情報なしで一時的なUIDを発行し、後から正規アカウントにリンキング(昇格)できる。 ↩
-
Firebase — Googleが提供するモバイルアプリ開発プラットフォーム。認証、データベース(Firestore)、ホスティング、プッシュ通知等を統合的に提供する。 ↩
-
Provisioning Profile — Apple開発者プログラムで発行される証明書。アプリの署名、デバイスの許可、Entitlements(権限)の設定を含む。 ↩
-
idevicesyslog — libimobiledeviceに含まれるCLIツール。USB接続したiPhoneのsyslog/ASLを取得する。
brew install libimobiledeviceでインストール。 ↩