3/2

2026

Firebase匿名ログインで「とりあえず使える」を実現する

#Firebase#iOS#認証#Swift#Anonymous AuthFirebase匿名ログインで「とりあえず使える」を実現する

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箇所だけ

  1. Firebase Consoleにアクセス
  2. プロジェクトを選択(PlayTimeの場合: playtime-69c21
  3. AuthenticationSign-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 → 匿名 → 有効にする。

エラーコード: 17098FIRAuthErrorCodeAdminRestrictedOperation)。コード側のバグではない。

二重呼び出しによる認証の重複

画面遷移やSwiftUIのライフサイクルでsignInAnonymouslyが複数回呼ばれると、毎回新しい匿名UIDが発行される可能性がある。

解決: isAuthenticatingフラグで排他制御。上記のPlayTimeコード例参照。

currentUserがnilなのにsignInAnonymouslyも失敗する

原因の可能性

  1. ネットワーク未接続(機内モードなど)
  2. Firebase SDKの初期化が完了していない(FirebaseApp.configure()が未呼び出し)
  3. 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ステップ。

  1. Firebase Console → Authentication → 匿名を有効化
  2. Auth.auth().signInAnonymously()を呼ぶ
  3. Firestoreルールでrequest.auth != nullを設定

これだけでユーザーはアカウント作成なしにアプリの全機能を使える。気に入ったらuser.link(with:)で正規アカウントに昇格。UIDもデータもそのまま引き継がれる。

自分の場合、PlayTimeのボス協力機能でこれを入れた。認証周りのトラブルはOSLogのmiyashi_AUTHフィルタで一発で追跡できるようになっているので、Notionに「認証エラー出てる」と書くだけであとはエージェントが勝手に解決してくれる。

Footnotes

  1. Firestore — Firebase上のNoSQLドキュメントデータベース。リアルタイム同期とオフラインサポートが特徴。セキュリティルールで認証状態に基づくアクセス制御が可能。

  2. Anonymous Authentication — Firebase認証の一機能。ユーザー情報なしで一時的なUIDを発行し、後から正規アカウントにリンキング(昇格)できる。

  3. Firebase — Googleが提供するモバイルアプリ開発プラットフォーム。認証、データベース(Firestore)、ホスティング、プッシュ通知等を統合的に提供する。

  4. Provisioning Profile — Apple開発者プログラムで発行される証明書。アプリの署名、デバイスの許可、Entitlements(権限)の設定を含む。

  5. idevicesyslog — libimobiledeviceに含まれるCLIツール。USB接続したiPhoneのsyslog/ASLを取得する。brew install libimobiledeviceでインストール。