Zero-Tap Sign-In Restorationが2027年4月必須化 — Restore Credentials APIの実装とFlutterの空白
はじめに
機種変更してアプリを開き直したとき、もう一度ログインさせられる。Google はこれを「アプリ品質の問題」として扱うことに決めた。2026年8月、Android Developers Blog で Google Play の新しい技術品質要件が発表され、その中に「Zero-Tap Sign-In Restoration」が含まれている。2027年4月から、サインイン機能を持つアプリはこれに対応していないと Play ストアでの公開能力と露出に影響を受ける。
対応手段として指定されているのが Android の Restore Credentials API だ。名前のとおり「復元専用の鍵」を扱う仕組みで、パスキーと同じサーバー側実装を流用できる設計になっている。この記事では要件の正確な範囲、Restore Credentials の仕組みと実装、そして見落としやすい制約を整理する。最後に、クロスプラットフォーム開発者にとって本題になる点 — pub.dev にこの API を露出しているパッケージが存在しない — を扱う。
要件の正確な範囲 — 2月と4月で別の話
まず、2027年に2つの別々の締め切りがあることを分けて理解しておきたい。Android Developers Blog の発表は2本の要件を同時に告知している。
2027年2月 — メモリ関連。対象は「Memory usage (Anonymous RSS + Swap)」「Bitmap memory usage」「DEX code optimization」の3つで、DEX 最適化については最低25%のカバレッジが示されている。閾値を満たさないアプリは Play での露出と公開能力が制限されうる。
2027年4月 — Zero-Tap Sign-In Restoration。Play Console ヘルプの文面はこうなっている。
From April 2027, apps that support user sign-in (optional or mandatory) must support Zero-Tap Sign-In restoration when a user moves from their previous Android device to a new one.
注目すべきは「optional or mandatory」の部分だ。ログインを必須にしているアプリだけでなく、任意のログイン機能を置いているだけのアプリも対象に入る。設定画面の片隅に「アカウント連携」があるだけのアプリも含まれると読める範囲だ。
除外されるのは次のケースである。
- ゲーム: 現時点では対象外。ただし Google は実装を推奨しており、複雑なゲーム認証向けの個別ガイダンスを2027年に出すとしている
- 完全非公開アプリ・エンタープライズ端末管理アプリ
- 厳格な規制・コンプライアンス要件を持つアプリ(金融・ヘルスケアなど): 施行前に Play Console から免除申請が可能
- Block Store 連携済みのアプリ: ただし期限付き
この Block Store の扱いが、今この記事を書いている時点で一番期限が近い項目だ。ヘルプには「Apps integrating with Block Store, on or before September 30, 2026, to restore a user’s sign-in state are considered compliant with this requirement」とあり、2026年9月30日までに Block Store 連携を完了していれば準拠扱いになる。裏を返せば、それ以降に完了した連携や他の方式は準拠と認められない。すでに Block Store で移行を実装している・実装中のチームには17日しか残っていない。
コンプライアンスの判定方法も明記されている。「Successful user sign in restoration is determined through the successful restore key retrieval」— つまり復元鍵の取得が成功したかどうかで測られる。実装したつもりでも、取得が実際に成功していなければカウントされない建付けだ。
Restore Credentials の仕組み — パスキーのサーバー実装を使い回す
Restore Credentials は Credential Manager の一機能で、扱う credential type は「restore key(復元鍵)」と呼ばれる。これは FIDO2 / パスキーのバックエンドと互換のある公開鍵だ。ここが設計上の要点で、すでにパスキーを実装しているならサーバー側はほぼそのまま流用できる。公式ドキュメントも、パスキー対応済みのアプリに特に推奨する旨を明記している。
フローは3段階に分かれる。
1. 旧端末で鍵を作る。 ユーザーがサインインした直後(およびすでにサインイン済みならアプリ起動時)に、復元鍵を生成する。ここが通常のパスキー登録と決定的に違うのは、UI が一切出ないこと。生体認証のプロンプトもダイアログも出ず、完全に裏で終わる。生成された鍵は Android Backup Service がローカルに保存し、ユーザーのバックアップ設定に応じてクラウドにも保存される。
2. 端末間を渡る。 ここに開発者の作業はない。鍵の転送は Android システムのバックアップ/復元機構に乗っており、クラウドバックアップ経由でも USB ケーブルでの端末間直接転送でも運ばれる。
3. 新端末で取り出す。 新端末でアプリが初めて開かれたとき、復元鍵を使ってサーバーに認証をかけ、サインイン状態を復帰させる。ユーザーの操作はゼロ — これが「Zero-Tap」の意味だ。
クラウドバックアップを効かせるには、ユーザー側に条件がある。Google アカウントにサインイン済み、Android のデータバックアップが有効、そして画面ロック(パターン/PIN/パスワード/生体)が設定されていること。これを満たさない状態で鍵を作ろうとすると E2eeUnavailableException が飛ぶ。
実装 — 3つの呼び出しと例外の扱い
依存関係は androidx.credentials とその Play Services 連携で、要件は Android 9(API 28)以上、androidx.credentials 1.5.0 以上、GMS core 24220000 以上。
鍵の作成は、サーバーから取得した WebAuthn 形式の PublicKeyCredentialCreationOptionsJSON を包んで createCredential() を呼ぶだけだ。
val createRestoreRequest = CreateRestoreCredentialRequest(
requestJson = serverCreationOptions,
isCloudBackupEnabled = true
)
try {
val response = credentialManager.createCredential(context, createRestoreRequest)
sendToServer(response.registrationResponseJson)
} catch (e: E2eeUnavailableException) {
// バックアップ条件を満たしていない → ローカル保存のみで再試行
val fallback = CreateRestoreCredentialRequest(
requestJson = serverCreationOptions,
isCloudBackupEnabled = false
)
sendToServer(
credentialManager.createCredential(context, fallback).registrationResponseJson
)
}
isCloudBackupEnabled の扱いは素直に見えて罠がある。false にした鍵はローカルにしか残らず、クラウドバックアップからの復元では新端末に渡らない。つまり E2eeUnavailableException を false で捕まえてフォールバックする上記のパターンは、「端末間直接転送なら効くが、クラウド復元では効かない鍵」を作っていることになる。例外を黙らせるための回避策ではなく、カバー範囲が違う2種類の鍵だと理解して使い分ける必要がある。
新端末側の取得は GetRestoreCredentialOption を使う。
val options = GetRestoreCredentialOption(authenticationJson)
val response = credentialManager.getCredential(context, GetCredentialRequest(listOf(options)))
val credential = response.credential as RestoreCredential
sendAuthToServer(credential.authenticationResponseJson)
呼ぶタイミングが2箇所推奨されている。端末での初回起動時、そして**BackupAgent.onRestoreFinished() の中**だ。後者は見落としやすい。アプリデータの復元が終わった直後に鍵を取りに行くことで、ユーザーが初めてアプリを開く前に認証済み状態を用意できる。起動時のみに仕込むと、復元完了とアプリ起動の間に取得タイミングを逃す余地が残る。
サインアウト時の削除も忘れてはならない。ClearCredentialStateRequest(credentialType = CredentialManager.TYPE_CLEAR_RESTORE_CREDENTIAL) を呼ぶ。アプリのアンインストールやデータ消去ではシステム側が自動で消すが、アプリ内のサインアウトは自前で消す必要がある。ここを落とすと、サインアウトしたはずのアカウントが新端末で自動復帰する挙動になる。
サーバー側では、復元鍵を通常のパスキーと区別して保存しておくことが推奨されている。認証ロジックは共通でよいが、アカウント管理 UI に「復元鍵」をパスキーとして並べてしまうと、ユーザーには意味不明な項目として見える。
設計を縛る3つの制約
ドキュメントに書かれているが実装前に見落としやすい制約が3つある。どれも後から設計変更が重い箇所に効いてくる。
単一アカウントのみ。 復元鍵はアプリあたり1つしか作成・復元されない。複数アカウントを切り替えて使う設計のアプリでは、プライマリまたは最終利用アカウントを1つ選ぶ必要がある。「全アカウントが復元される」前提で UX を設計すると破綻する。
システムプロファイルを越えない。 仕事用/個人用プロファイルが分かれている端末では、復元鍵は最初にセットアップされたプロファイルにのみ提供される。業務アプリでプロファイルを前提にしている場合、復元が効かない組み合わせが存在する。
フォームファクタを越えない。 スマートフォン→タブレット、スマートフォン→Wear OS といった移行では機能しない。要件自体がモバイルとタブレットのサインイン復元を扱う文脈なので、「タブレットへの移行も自動化できる」とは読まないほうがいい。
加えて、復元鍵が扱うのは基本的な身元の復元だけだ。多要素認証、OAuth スコープ、二次的な認可は対象外で、これらは別途 just-in-time な権限フローで処理する前提になっている。「復元鍵を入れれば認証周りが全部移る」わけではない。
Flutter の空白 — パッケージが存在しない
ここがクロスプラットフォーム開発者にとっての本題だ。2026年9月13日、r/FlutterDev に「Plugin for new Android requirement: Zero-Tap Sign-In Restoration」という投稿が上がった。投稿者は、この要件に対応する Flutter プラグインが見当たらないため自作を進めている、という趣旨のことを書いている。
実際に pub.dev の状況を確認すると、主張は裏付けられる。Credential Manager 系のパッケージとしては credential_manager(最新 5.1.0、活発に更新されている)とそのフォークである flutter_credential_manager があるが、どちらも対応しているのはパスキー・パスワード・フェデレーテッドサインイン(Sign in with Google)までで、CreateRestoreCredentialRequest / GetRestoreCredentialOption に相当する API は公開されていない。google_sign_in_all_platforms の silentSignIn() は保存済み資格情報でのサイレントサインインを提供するが、これは Android の復元鍵機構とは別物で、Play が判定基準に据える「復元鍵の取得成功」にはならない。
つまり現状、Flutter アプリでこの要件を満たすには Platform Channel で Kotlin 側を自分で書くことになる。幸い、実装の性質上これはそこまで重い作業ではない。UI を持たず、呼び出しは「作成」「取得」「削除」の3つだけで、やり取りするのは WebAuthn 形式の JSON 文字列だ。Dart 側に返すのも文字列で済むため、Platform Channel の境界は素直に引ける。むしろ重いのは サーバー側で、パスキーのバックエンドがまだ無い場合はそこから作る必要がある。
残り時間の見積もりも書いておく。2027年4月まで約19ヶ月ある。長いように見えるが、認証は後付けが重い領域だ。サーバー側に FIDO2 のエンドポイントが無い、アカウント設計が複数アカウント前提になっている、プロファイル跨ぎの業務要件がある — こうした条件のどれかに当たると、着手から完了までの距離は一気に伸びる。今すぐやるべきは実装ではなく、上記3制約に自分のアプリが抵触するかの棚卸しだろう。そして Block Store で実装中のチームは、9月30日という別の期限が先に来ていることを確認したほうがいい。
まとめ
- 2027年4月から、サインイン機能を持つ Android アプリ(任意ログインを含む)は Zero-Tap Sign-In Restoration 対応が Play の要件になる。未対応は公開能力と露出に影響する
- 対応手段は Restore Credentials API(Android 9以上、
androidx.credentials1.5.0以上)。パスキーとサーバー実装を共用できる設計で、UI は一切出ない - Block Store 連携は2026年9月30日までに完了した分のみ準拠扱い。実装中のチームはこちらの期限が先に来る
- 制約は3つ — アプリあたり1アカウント、最初にセットアップしたプロファイルのみ、フォームファクタを越えない。多要素認証や OAuth スコープは対象外
- pub.dev に Restore Credentials を露出する Flutter パッケージは存在しない。現状は Platform Channel で自作する必要がある
判定が「復元鍵の取得成功」という実測ベースになっている点は重く受け取るべきだ。実装したかどうかではなく、実際にユーザーの端末で復元が成功しているかが見られる。Flutter 側は誰かがプラグインを出すのを待つ選択もあるが、2027年2月のメモリ要件と合わせて Play の品質要件が実測ベースに寄っていく流れだとすれば、待って様子を見るより棚卸しを先に済ませておくほうが安全だろう。
ソース
- Play Console technical quality requirements — Play Console ヘルプ
- Elevating app quality: Reducing memory usage and improving device migration — Android Developers Blog
- About Restore Credentials — Android Developers
- Implement Restore Credentials with Credential Manager — Android Developers
- Plugin for new Android requirement: Zero-Tap Sign-In Restoration — Reddit r/FlutterDev
- credential_manager — pub.dev
※ Reddit の投稿本文・コメントは取得できなかったため、スレッドの内容についてはデイリーダイジェストの要約に基づく。pub.dev のパッケージ状況は個別に確認した。