🍎

[Flutter] Firebase Appleログインのアカウント削除とrevoke対応をアプリ側だけでやる方法

2024/03/03に公開

背景

Appleログインしているユーザーが退会する時、Appleの規約上

  1. アカウント削除できる導線がアプリ内に存在している
  2. アカウント削除したときに /auth/revokeエンドポイントにリクエストしてユーザートークンを無効化する

の要件を満たす必要があります.

2022年に実装した時はサーバーからApple Revokeエンドポイントへリクエストを送る実装をしていたのですが、2023.08からfirebase_authにrevoke用のメソッドが追加されていたので、フロントエンドから直接revokeすることにしました.

  • revokeTokenWithAuthorizationCodeメソッドの定義
  /// Apple only. Users signed in with Apple provider can revoke their token using an authorization code.
  /// Authorization code can be retrieved on the user credential i.e. userCredential.additionalUserInfo.authorizationCode
  Future<void> revokeTokenWithAuthorizationCode(String authorizationCode) {
    return _delegate.revokeTokenWithAuthorizationCode(authorizationCode);
  }

https://github.com/firebase/flutterfire/blob/master/packages/firebase_auth/firebase_auth/lib/src/firebase_auth.dart#L825

対応手順

公式からの動画がわかりやすかったです。
https://youtu.be/U1PIrZBgv0U?si=0cDvZ20csmQWGnPM

FirebaseがApple Developerにアクセスできるようにするためのキーを作成&登録する

FirebaseのAuthenticationページへ行き、Appleログインの項目を開くと、

  1. Services ID(サービスID)
  2. OAuth code flow configuration(OAuthコードフローの構成)

という入力欄があります. この2箇所を埋める必要があります.

サービスIDの登録

Apple Developer Consoleの「Certificates, Identifiers & Profiles」のページに移動しサービスIDを作成します.
作成後に編集し、「Sign In with Apple」を有効化します.

Firebase Consoleからドメイン名とReturn URLを取得して、サービスIDに登録します.

Firebase Consoleに戻り、先ほど作成したサービスIDを登録します.

OAuthコードフローの登録

Apple Developer Consoleに移動し、Keyを作成しダウンロードします.
一度しかダウンロードできないので注意です.

Firebase Consoleに戻り、

  1. Apple Team ID
  2. 先ほど作成したKeyのID
  3. ダウンロードしたKeyの中身

を登録します.

アカウント削除の実装

削除するときの要件を整理すると以下のようになります.

  1. アカウント削除前に再度ログインしてAuthorization Codeを取得する
  2. 上記のAuthorization Codeを使ってrevokeリクエストをする
  3. Firebaseのアカウントを削除する
  4. Firebaseからサインアウトする
  5. FirebaseのcurrentUserをリロードしてセッションをリフレッシュする

注意点は以下です.
revokeしてからFirebaseのアカウントを消す必要があります.
サインアウト&リロードをして認証状態を明示的に更新しましょう.セッションが残ってしまう場合があります.

class AuthService {
  AuthService(this._firebaseAuthRepository);
  final FirebaseAuthRepository _firebaseAuthRepository;

  Future<bool> deleteAppleAccount() async {
    try {
      // 1. 再認証
      final userCredential =
          await _firebaseAuthRepository.reauthenticateWithProvider(
        AppleAuthProvider(),
      );
      if (userCredential == null) {
        return false;
      }

      // 2. Revoke
      await _firebaseAuthRepository.revokeTokenWithAuthorizationCode(
        userCredential.additionalUserInfo!.authorizationCode!,
      );

      // 3. Firebasアカウントを削除
      await _firebaseAuthRepository.deleteAccount();

      // 4. サインアウトしてセッションリフレッシュ
      await _firebaseAuthRepository.signOut();

      // 5. currentUserをリロードする
      unawaited(_firebaseAuthRepository.reload());

      return true;
    } on Exception {
      rethrow;
    }
  }
}
class FirebaseAuthRepository {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  Future<UserCredential?> reauthenticateWithProvider(
    AuthProvider provider,
  ) async {
    try {
      return await _auth.currentUser?.reauthenticateWithProvider(provider);
    } on FirebaseAuthException {
      rethrow;
    } on Exception {
      rethrow;
    }
  }

  Future<void> revokeTokenWithAuthorizationCode(
    String authorizationCode,
  ) async {
    // codeはuserCredential!.additionalUserInfo!.authorizationCode
    try {
      await _auth.revokeTokenWithAuthorizationCode(authorizationCode);
    } on Exception {
      rethrow;
    }
  }

  Future<void> deleteAccount() async {
    try {
      await _auth.currentUser?.delete();
    } on Exception {
      rethrow;
    }
  }

  Future<void> signOut() async {
    try {
      await _auth.signOut();
    } on Exception {
      rethrow;
    }
  }

  Future<void> reload() async {
    try {
      await _auth.currentUser?.reload();
    } on FirebaseAuthException {
      rethrow;
    } on Exception {
      rethrow;
    }
  }
}

revokeできていることの動作確認方法

動作確認は2つの方法で可能です.

  1. Appleからメールが来る
  2. 設定画面から確認する

Revokeが成功したらすぐに下記のようなメールが届きます.

実機でビルドしている場合は、設定アプリの「Apple IDを使用中のアプリ」から該当アプリが消えていることを確認できます.

Discussion