カスタムのアカウントの種類を作成する

ここまでは、Google が定義したアカウントとユーザーを使用する Google API へのアクセスについて説明してきました。独自のオンライン サービスを運用している場合、Google アカウントやユーザーは存在しません。では、どうすればよいでしょうか。ユーザーのデバイスに新しいアカウント タイプをインストールするのは比較的簡単です。このレッスンでは、組み込みアカウントと同じように機能するカスタム アカウント タイプを作成する方法について説明します。

カスタム アカウントのコードを実装する

最初に必要なのは、ユーザーから認証情報を取得する方法です。これは、名前とパスワードを要求するダイアログ ボックスのような単純なものでもかまいません。あるいは、ワンタイム パスワードや生体認証スキャンのような、より独特な手順の場合もあります。いずれの場合も、デベロッパーは以下のコードを実装する必要があります。

  1. ユーザーから認証情報を収集する
  2. サーバーで認証情報を認証する
  3. デバイスに認証情報を保存する

通常、これら 3 つの要件はすべて 1 つのアクティビティで処理できます。これを認証システム アクティビティと呼びます。

認証システム アクティビティは AccountManager システムとやり取りする必要があるため、通常のアクティビティにはない特定の要件があります。簡単に処理できるようにするため、Android フレームワークには基本クラスである AccountAuthenticatorActivity が用意されています。このクラスを拡張して独自のカスタム認証システムを作成できます。

認証システム アクティビティの最初の 2 つの要件(認証情報の収集と認証)にどう対応するかは、完全に自由に決めることができます。(方法が 1 つしかない場合、「カスタム」アカウント タイプを使用する必要はありません)。3 つ目の要件は、正規のシンプルな実装です。

Kotlin

Account(username, your_account_type).also { account ->
    accountManager.addAccountExplicitly(account, password, null)
}

Java

final Account account = new Account(username, your_account_type);
accountManager.addAccountExplicitly(account, password, null);

セキュリティにご注意!

AccountManager は暗号化サービスやキーチェーンではないことを理解することが重要です。アカウント認証情報は、渡した際と同様に書式なしテキストで保存されます。ほとんどのデバイスでは、ルートのみがアクセスできるデータベースに保存されるため、これは特に問題にはなりません。ユーザーに root 権限のあるデバイスでは、デバイスへの adb アクセス権を持つすべてのユーザーが認証情報を読み取ることができます。

その点を考慮すると、ユーザーの実際のパスワードを AccountManager.addAccountExplicitly() に渡さないでください。代わりに、攻撃者による使用が限定される、暗号的に安全なトークンを保存する必要があります。ユーザー認証情報で価値のあるものを保護している場合は、同様の処理を慎重に検討する必要があります。

注意: セキュリティ コードに関しては、「誤解の解消」のルールに従ってください。自宅では試さないでください。カスタム アカウント コードを実装する前に、セキュリティの専門家にご相談ください。

セキュリティに関する免責条項がなくなったので、さっそく本題に戻りましょう。カスタム アカウント コードの構成要素はすでに実装済みです。あとは作業を行うだけです。

AbstractAccountAuthenticator を拡張する

AccountManager をカスタム アカウント コードで機能させるには、AccountManager が要求するインターフェースを実装するクラスが必要です。このクラスが認証システムクラスです。

認証システムクラスを作成する最も簡単な方法は、AbstractAccountAuthenticator を拡張して抽象メソッドを実装することです。これまでのレッスンで見たことのある AbstractAccountAuthenticator の抽象メソッドは見覚えがあるかもしれません。アカウント情報と認可トークンを取得するために前のレッスンで呼び出したメソッドとは反対のものです。

認証システムクラスを適切に実装するには、多数の個別のコードが必要です。まず、AbstractAccountAuthenticator にはオーバーライドする必要がある抽象メソッドが 7 つあります。次に、"android.accounts.AccountAuthenticator"インテント フィルタをアプリケーション マニフェストに追加する必要があります(次のセクションで説明します)。最後に、カスタム アカウント タイプの名前と、システムがこのタイプのアカウントの横に表示するアイコンを定義する 2 つの XML リソースを指定する必要があります。

認証システムクラスと XML ファイルを適切に実装するための手順ガイドについては、AbstractAccountAuthenticator ドキュメントをご覧ください。

認証システム アクティビティに特別な初期化パラメータが必要な場合は、Intent.putExtra() を使用してインテントにアタッチできます。

認証システム サービスを作成する

認証システムクラスを作成したので、それを動作させる場所が必要です。アカウント認証システムは、複数のアプリケーションで利用可能で、バックグラウンドで動作させる必要があるため、当然ながら Service 内で実行する必要があります。これを認証システム サービスと呼ぶことにします。

認証システム サービスは非常に簡単です。onCreate() で認証システムクラスのインスタンスを作成し、onBind()getIBinder() を呼び出すだけです。

必ずマニフェスト ファイルに <service> タグを追加し、AccountAuthenticator インテントのインテント フィルタを追加して、アカウント認証システムを宣言してください。

<service ...>
   <intent-filter>
      <action android:name="android.accounts.AccountAuthenticator" />
   </intent-filter>
   <meta-data android:name="android.accounts.AccountAuthenticator"
             android:resource="@xml/authenticator" />
</service>

サービスを配布する

これで完了です。これで、「Google」や「法人」などのすべての有名アカウント タイプに加え、アカウントの種類がシステムで認識されるようになりました。[アカウントと同期] 設定ページを使用してアカウントを追加できます。カスタムタイプのアカウントを要求するアプリは、他のアカウント タイプの場合と同様に列挙と認証を行うことができます。

もちろん、これらはすべて、アカウント サービスが実際にデバイスにインストールされていることを前提としています。1 つのアプリがサービスにアクセスするだけで済む場合、これは大事なことではありません。アプリにサービスをバンドルするだけです。ただし、アカウント サービスを複数のアプリで使用する場合は、事態は複雑になります。サービスをすべてのアプリにバンドルして、複数のコピーがユーザーのデバイスの容量を占有しないようにする場合は、

1 つの解決方法は、このサービスを 1 つの小さくて特別な APK に入れることです。アプリがカスタム アカウント タイプを使用するときに、デバイスをチェックして、カスタム アカウント サービスが使用可能かどうかを確認します。存在しない場合は、ユーザーを Google Play に誘導してサービスをダウンロードしてもらいます。最初は大変に思われるかもしれませんが、カスタム アカウントを使用するすべてのアプリに対して認証情報を再入力するのではなく、とても簡単です。