はじめに
自分で開発しているアプリに以下の3rd-party製の認証をつけてリリース申請をしたところ、Appleから以下のような返事が来た。
つまり、他のアカウントとの連携認証システム入れるならAppleアカウントとの連携認証も入れなさい、とのこと。
現状は以下のサービスのアカウントとの連携認証を実装しているので、これにAppleアカウントとの連携認証機能も追加していく。
- MailAddress/Password
- Google account
- Twitter account
ちなみに上記のサービスのアカウントとの連携認証に関しては、以下の記事に記述している。
実装
Flutter packageでapple sign in
で調べたら何個かヒットした。
その中で一番likeが多く、READMEも丁寧に書かれていてやりやすそうな以下のパッケージを選択。
基本的にREADMEを読めば問題はないが、英語読むのめんどい時のためにメモ。
大まかな流れは以下。
- apple developer programでアプリのbundleIdを登録(既にしている場合はスキップ)
- bundleIdに、
sign in with apple
を許可する - FirebaseAuthenticationで、appleでのサインインを設定
- xcodeへの導入
- コードを修正
1. アプリのBundleIdを登録
page: Apple Developer: Certificates, Identifiers & Profiles
すでにあったら大丈夫。
2. sign in with apple
を許可
1.のページから、該当アプリのBundleIdを選択し、クリック。
そこで、Sign in with Apple
を有効化する。
ユーザ情報の変更通知等を受け取らなくていい場合は、下の部分は気にしないで良い。
3. Firebase Authenticationの設定
iPhoneアプリからしか使わない場合は、下は無視して良い。
4. xcodeへの導入
該当アプリのrunner.xcodeprj
ファイルをxcodeで開き、Capability
をクリックし、Sign in With Apple
を検索しクリック。
そうすると、画像のようにSign in With Apple
がアプリに追加される。
5. コードの修正
以下のブランチが、今回の変更を行っているブランチ。
以下のPRを見ると、差分がわかりやすいかもしれない。
パッケージの導入
dependencies:
flutter:
sdk: flutter
# 以下追加
sign_in_with_apple: ^3.3.0
パッケージの利用
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
// ログインメソッド
Future<void> _onSignInWithApple(User? user) async {
try{
// AuthorizationCredentialAppleIDのインスタンスを取得
final appleCredential = await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
);
// OAthCredentialのインスタンスを作成
OAuthProvider oauthProvider = OAuthProvider('apple.com');
final credential = oauthProvider.credential(
idToken: appleCredential.identityToken,
accessToken: appleCredential.authorizationCode,
);
if (user !=null && user.isAnonymous) {
await user.linkWithCredential(credential);
Navigator.of(context).pop();
} else {
await FirebaseAuth.instance.signInWithCredential(credential);
if (Navigator.of(context).canPop()) {
Navigator.of(context).pop();
} else {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) => PhotoListScreen(),
),
);
}
}
} catch(e) {
await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('エラー'),
content: Text(e.toString()),
);
}
);
}
}
// コンポーネント
if (Platform.isIOS) SignInButton(
Buttons.Apple,
onPressed: () {
_onSignInWithApple(user);
},
)
UIパッケージの利用
Buttonの見た目には、flutter_signin_button
というパッケージを用いている。
Credentialの扱い
sign_in_with_apple
を用いて取得したAuthorizationCredentialAppleID
は、そのままfirebaseに渡すことはできない。
Firebaseに渡す用に、AuthorizationCredentialAppleID
から情報を抜き出してOAthCredential
のインスタンスを作成しなければならない。
// AuthorizationCredentialAppleIDのインスタンスを取得
final appleCredential = await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
);
// OAthCredentialのインスタンスを作成
OAuthProvider oauthProvider = OAuthProvider('apple.com');
final credential = oauthProvider.credential(
idToken: appleCredential.identityToken,
accessToken: appleCredential.authorizationCode,
);
ちなみにAuthorizationCredentialAppleID
に含まれる情報は以下。
@immutable
class AuthorizationCredentialAppleID {
/// Creates an instance which contains the result of a successful Sign in with Apple flow.
const AuthorizationCredentialAppleID({
@required this.userIdentifier,
@required this.givenName,
@required this.familyName,
required this.authorizationCode,
@required this.email,
@required this.identityToken,
@required this.state,
});
...
どの変数が何を意味しているかは、以下の記事をみるとなんとなくわかる。
完全にはわからないが、動いたからよしとしている。
既知の問題
ちなみに、simulatorでいくら試してもこのコードは動かない(2022/2/3時点)。
githubディレクトリ飛んでissueでも作って文句言ってやろうかと思ったら、すでにあった。
どうやらsimulatorの問題らしく、実機でテストをするときちんと動くことが確認できた。
まとめ
今回は、appleから直々に怒られたので、appleアカウント連携認証を実装してみた。
全然難しくない変更ではあるが、simulatorの問題でめちゃくちゃ引っかかってしまった。
xcode周りは挙動が怪しいので、コードを疑いつつソフトウェアそのものにも疑いを持つようにしよう。