[Flutter] Firebase Appleログインのアカウント削除とrevoke対応をアプリ側だけでやる方法
背景
Appleログインしているユーザーが退会する時、Appleの規約上
- アカウント削除できる導線がアプリ内に存在している
- アカウント削除したときに
/auth/revoke
エンドポイントにリクエストしてユーザートークンを無効化する
の要件を満たす必要があります.
-
Appleログインのアカウント削除に関する要件
https://developer.apple.com/forums/thread/708415?login=true -
POST https://appleid.apple.com/auth/revoke
https://developer.apple.com/documentation/sign_in_with_apple/revoke_tokens
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);
}
-
Firebaseのrevoke関連プルリク
https://github.com/firebase/flutterfire/issues/11065 -
FirebaseのChangeLog
https://github.com/firebase/flutterfire/blob/master/CHANGELOG.md#2023-08-23
対応手順
公式からの動画がわかりやすかったです。
FirebaseがApple Developerにアクセスできるようにするためのキーを作成&登録する
FirebaseのAuthenticationページへ行き、Appleログインの項目を開くと、
- Services ID(サービスID)
- 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に戻り、
- Apple Team ID
- 先ほど作成したKeyのID
- ダウンロードしたKeyの中身
を登録します.
アカウント削除の実装
削除するときの要件を整理すると以下のようになります.
- アカウント削除前に再度ログインしてAuthorization Codeを取得する
- 上記のAuthorization Codeを使ってrevokeリクエストをする
- Firebaseのアカウントを削除する
- Firebaseからサインアウトする
- 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つの方法で可能です.
- Appleからメールが来る
- 設定画面から確認する
Revokeが成功したらすぐに下記のようなメールが届きます.
実機でビルドしている場合は、設定アプリの「Apple IDを使用中のアプリ」から該当アプリが消えていることを確認できます.
Discussion