2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CredentialManagerを利用した認証情報の自動補完について

Last updated at Posted at 2025-12-17

こんにちは。ZOZO Advent Calendar 2025の18日目シリーズ3の担当をさせてもらいます。今回はAndroidアプリでの認証情報の自動補完機能を実現してみたので、それらに関連したまとめ記事を書きたいと思います。なお2025年8月時点なので最新ではないところはご了承ください。

はじめに

認証情報の自動補完についてですが、自明だと思いますがメールアドレスやパスワードなどの認証情報の自動入力機能を指します。これらの入力体験はユーザにとっては面倒だったり、または忘れていて思い出す必要があったりと時間的コストがかかる部分だと思います。また、開発者であればサインイン-アウトの確認を何度もやる必要がある場合、何度も入力する手間があったりします。これらの時間を大幅に省くことが大きなメリットと考えられます。

対応概要

ドキュメントをみてもらうとわかりますが実装は至ってシンプルです。TextFieldのModifier.semantics { contentType = ContentType.Username } を設定するとAutofillが動作する、と記述があります。また、旧来のAutofillNodeやAutofillTypeを使った補完は非推奨となりました。また、semanticsを指定しただけではうまく補完されないこともあるため、CredentialManagerを利用する実装を加えました。このCredentialManagerを利用するためにはDAL(Digital Asset Links)の設定が必要であり、後述で書きますが、こちらの対応が若干手間取りました。

実装について

導入

バージョンカタログに以下の依存関係を追加します。実装側のgradleにもこちらを追加します。

androidx-credentials = "1.5.0"
google-play-services-identity = "18.1.0"
androidx-credentials = { module = "androidx.credentials:credentials", version.ref = "androidx-credentials" }
androidx-credentials-play-services-auth = { module = "androidx.credentials:credentials-play-services-auth", version.ref = "androidx-credentials" }
google-play-services-identity = { module = "com.google.android.gms:play-services-identity", version.ref = "google-play-services-identity" }

asset_statements.xmlをvalues配下に追加します。後述で記載しますが、assetLinks.jsonのパスキーを明示したファイルとなります。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="asset_statements" translatable="false">
        [{
            \"include\": \"https://example.com/.well-known/assetlinks.json\"
        }]
    </string>
</resources>

AndroidManifestにも追加します。

<meta-data
    android:name="asset_statements"
    android:resource="@string/asset_statements" />

実装詳細

Compose側の実装はTextFieldにsemanticsを追加するぐらいです。

TextField(
    value = email,
    onValueChange = { updateEmail(it) },
    modifier = Modifier.semantics { contentType = ContentType.Username },
    // ...
)
TextField(
    value = password,
    onValueChange = { updatePassword(it) },
    modifier = Modifier.semantics { contentType = ContentType.Password },
    // ...
)

サインイン画面の起動時に認証情報の読み込みと次画面遷移時に保存を実行したいため、Fragment側にCredentialManager 関連の処理を実装しました。

    private val credentialManager: CredentialManager by lazy {
        CredentialManager.create(requireContext())
    }

    // 認証情報の保存
    private fun saveCredential(callback: () -> Unit) {
        // ViewModelなどで認証情報の保存が必要かどうか
        if (viewModel.filledByCredentialManager) {
            callback()
            return
        }
        // 必要がある場合は保存を実行
        viewLifecycleOwner.lifecycleScope.launch {
            try {
                credentialManager.createCredential(
                    requireActivity(),
                    CreatePasswordRequest(
                        viewModel.email,
                        viewModel.password,
                    ),
                )
            } catch (e: Exception) {
                Timber.e(e, "saveCredential error")
            } finally {
                callback()
            }
        }
    }
    // 認証情報の読み込み
    private fun loadCredential() {
        // フォームがすでに入力されている場合は認証情報の要求はしない
        if (viewModel.email.isNotEmpty() || viewModel.password.isNotEmpty()) {
            return
        }
        viewLifecycleOwner.lifecycleScope.launch {
            try {
                val req = GetCredentialRequest(listOf(GetPasswordOption()))
                val res = credentialManager.getCredential(requireActivity(), req)
                when (val credential = res.credential) {
                    is PasswordCredential -> {
                        viewModel.onAction(Action.UpdateCredential(credential.id, credential.password))
                        viewModel.onAction(Action.SignInClicked)
                    }
                    else -> {
                        Timber.i("receive other type: $credential")
                    }
                }
            } catch (e: Exception) {
                Timber.w(e, "loadCredential error")
            }
        }
    }

CredentialManagerによる認証情報の読み込みに成功するとGoogleパスワードマネージャが起動して入力候補を表示してくれます。選択をすると保存した認証情報を返してくれます。

また、認証情報の保存に関しての注意点ですが、Logでワーニングが出たため同じ認証情報を繰り返して保存しない実装にしたほうが良さそうです(もしかすると当時の実装方法に誤りがあったかもしれません)。

DALの設定について

ここからは開発に関してではなく導入時の対応についての記載となります。実装のみではCredentialManagerが正常動作しないためDAL(Digital Asset Links)を設定する必要があります。具体的には以下のようなassetLinks.jsonをWebに配置します。

[
  {
    "relation" : [
      "delegate_permission/common.handle_all_urls",
      "delegate_permission/common.get_login_creds"
    ],
    "target" : {
      "namespace" : "android_app",
      "package_name" : "com.example.android",
      "sha256_cert_fingerprints" : [
        SHA_HEX_VALUE
      ]
    }
  }
]

relationに関しては特に触らずこのままです。targetに関して注意点としてはnamespace はこのままandroid_appとして登録しておきます。package_namesha256_cert_fingerprints に関しては設定したいアプリのパッケージ名と署名証明書のフィンガープリントを指定します。

手間取った点について

Webに配置する必要があるためアプリチームだけでは作業が完結できず、フロントエンドのチームの協力が必要でした。結論から言うと、assetLinks.jsonを設置してもらうためのコミュニケーションを何往復もさせてしまったことが反省点です。

  1. assetLinks.jsonの形式間違いが多々あり何度もパスキーの設置依頼をしてしまった
  2. DALについての知見が少なくフロントエンドチームへの説明不足で誤った対応を色々させてしまった
  • 1についての失敗
    assetLinks.jsonに不要なコメントをいれてしまった
    namespace にパッケージ名を入れてしまいエラーが返ってきた
  • 2についての失敗
    説明不足で https://domain/.well-known/assetlinks.json 形式ではないパスキーに設置させてしまった
    不要にも関わらず各開発環境にassetLinks.jsonの設置を依頼(本番環境以外不要だった)

といったように基本はDALへの知見不足が原因で起きた問題でした。
なお、assetLinks.jsonが問題なくアクセスできるかなどの検証はこちらで確認しました。

まとめ

今回はAndroidアプリにパスワード自動補完を実現させるため、CredentialManagerの実装概要とDALの設置までの流れといくつかの反省点について書かせてもらいました。パスワード自動補完の機能に関してはUX向上はもちろんのこと、開発でもデバッグしやすくなったと思います。それでは何かの参考になれば幸いです。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?