パスキーを作成する

ユーザーがパスキーで認証できるようにするには、アプリでまずアカウントのパスキーを登録または作成する必要があります。

パスキーを作成するには、アプリサーバーからパスキーの作成に必要な詳細情報を取得し、Credential Manager API を呼び出します。これにより、公開鍵と秘密鍵のペアが返されます。返された秘密鍵は、Google パスワード マネージャーなどの認証情報プロバイダにパスキーとして保存されます。公開鍵はアプリサーバーに保存されます。

パスキーは認証情報プロバイダに保存され、公開鍵はアプリサーバーに保存されます
図 1: パスキーの作成

前提条件

デジタル アセットリンクを設定し、Android 9(API レベル 28)以上を搭載したデバイスを対象にしていることを確認します。

概要

このガイドでは、パスキーを作成するために証明書利用者クライアント アプリで必要な変更に焦点を当て、証明書利用者アプリサーバーの実装の概要を説明します。サーバーサイド統合について詳しくは、サーバーサイドのパスキー登録をご覧ください。

  1. アプリに依存関係を追加する: 必要な Credential Manager ライブラリを追加します。
  2. 認証情報マネージャーをインスタンス化する: 認証情報マネージャー インスタンスを作成します。
  3. アプリサーバーから認証情報の作成オプションを取得する: アプリサーバーから、アプリ、ユーザー、challenge、その他のフィールドに関する情報など、パスキーの作成に必要な詳細情報をクライアント アプリに送信します。
  4. パスキーをリクエストする: アプリで、アプリサーバーから受け取った詳細情報を使用して GetPublicKeyCredentialOption オブジェクトを作成し、このオブジェクトを使用して credentialManager.getCredential() メソッドを呼び出してパスキーを作成します。
  5. パスキー作成レスポンスを処理する: クライアント アプリで認証情報を受け取ったら、エンコード、シリアル化してから、公開鍵をアプリサーバーに送信する必要があります。また、パスキーの作成時に発生する可能性のある各例外を処理する必要があります。
  6. サーバーで公開鍵を検証して保存する: サーバーサイドの手順を完了して、認証情報のオリジンを検証し、公開鍵を保存します。
  7. ユーザーに通知する: パスキーが作成されたことをユーザーに通知します。

1. アプリに依存関係を追加する

アプリ モジュールの build.gradle ファイルに次の依存関係を追加します。

Kotlin

dependencies {
    implementation("androidx.credentials:credentials:1.6.0-beta03")
    implementation("androidx.credentials:credentials-play-services-auth:1.6.0-beta03")
}

Groovy

dependencies {
    implementation "androidx.credentials:credentials:1.6.0-beta03"
    implementation "androidx.credentials:credentials-play-services-auth:1.6.0-beta03"
}

2. 認証情報マネージャーをインスタンス化する

アプリまたはアクティビティのコンテキストを使用して CredentialManager オブジェクトを作成します。

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)

3. アプリサーバーから認証情報作成オプションを取得する

ユーザーが [パスキーを作成] ボタンをクリックしたとき、または新規ユーザーが登録したときに、アプリからアプリサーバーにリクエストを送信して、パスキー登録プロセスを開始するために必要な情報を取得します。

アプリサーバーで FIDO 準拠のライブラリを使用して、クライアント アプリにパスキーの作成に必要な情報(ユーザー、アプリ、追加の構成プロパティに関する情報など)を送信します。詳しくは、サーバーサイドのパスキー登録をご覧ください。

クライアント アプリで、アプリサーバーから送信された公開鍵作成オプションをデコードします。通常、これらは JSON 形式で表されます。ウェブ クライアントでこのデコードがどのように行われるかについて詳しくは、エンコードとデコードをご覧ください。Android クライアント アプリの場合、デコードは個別に処理する必要があります。

次のスニペットは、アプリサーバーから送信される公開鍵作成オプションの構造を示しています。

{
  "challenge": "<base64url-encoded challenge>",
  "rp": {
    "name": "<relying party name>",
    "id": "<relying party host name>"
  },
  "user": {
    "id": "<base64url-encoded user ID>",
    "name": "<user name>",
    "displayName": "<user display name>"
  },
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -7
    }
  ],
  "attestation": "none",
  "excludeCredentials": [
    {
        "id": "<base64url-encoded credential ID to exclude>", 
        "type": "public-key"
    }
  ],
  "authenticatorSelection": {
    "requireResidentKey": true,
    "residentKey": "required",
    "userVerification": "required"
  }
}

公開鍵の作成オプションの主なフィールドは次のとおりです。

  • challenge: リプレイ攻撃を防ぐために使用されるサーバー生成のランダムな文字列。
  • rp: アプリの詳細。
    • rp.name: アプリの名前。
    • rp.id: アプリのドメインまたはサブドメイン。
  • user: ユーザーの詳細。
    • id: ユーザーの一意の ID。メールアドレスやユーザー名などの個人情報はこの値に含まれません。ランダムな 16 バイトの値を使用できます。
    • name: ユーザーが認識できるアカウントの一意の識別子(メールアドレスやユーザー名など)。この識別子はアカウント選択画面に表示されますユーザー名を使用する場合は、パスワード認証の値と同じ値を使用します。
    • displayName: アカウント セレクタに表示するための、わかりやすいアカウント名(省略可)。
  • authenticatorSelection: 認証に使用されるデバイスの詳細。

    • authenticatorAttachment: 優先される認証システムを示します。指定できる値は次のとおりです。 - platform: この値は、指紋認証センサーなど、ユーザーのデバイスに組み込まれている認証システムに使用されます。- cross-platform: この値は、セキュリティ キーなどのローミング デバイスに使用されます。通常、パスキー コンテキストでは使用されません。- 未指定(推奨): この値を未指定のままにすると、ユーザーは希望するデバイスでパスキーを柔軟に作成できます。ほとんどの場合は、パラメータを指定しないでおくことをおすすめします。
      • requireResidentKey: パスキーを作成するには、この Boolean フィールドの値を true に設定します。
      • residentKey: パスキーを作成するには、値を required に設定します。
      • userVerification: パスキー登録時のユーザー確認の要件を指定するために使用されます。指定可能な値は次のとおりです。 - preferred: ユーザー認証が保護よりも摩擦を引き起こす環境など、保護よりもユーザー エクスペリエンスを優先する場合は、この値を使用します。- required: デバイスで利用可能なユーザー認証メソッドの呼び出しが必要な場合は、この値を使用します。- discouraged: ユーザー確認方法の使用が推奨されない場合は、この値を使用します。
        userVerification の詳細については、userVerification の詳細をご覧ください。
  • excludeCredentials: 同じ認証情報プロバイダのパスキーがすでに存在する場合に重複するパスキーが作成されないように、配列に認証情報 ID をリストします。

4. パスキーをリクエストする

サーバーサイドの公開鍵作成オプションを解析したら、これらのオプションを CreatePublicKeyCredentialRequest オブジェクトでラップして createCredential() を呼び出し、パスキーを作成します。

createPublicKeyCredentialRequest には次の項目が含まれます。

  • requestJson: アプリサーバーから送信された認証情報作成オプション。
  • preferImmediatelyAvailableCredentials: これは、セキュリティ キーやハイブリッド キーフローの認証情報ではなく、ローカルで利用可能な認証情報または認証情報プロバイダで同期された認証情報のみを使用してリクエストを実行するかどうかを定義する、省略可能なブール値フィールドです。使用方法は次のとおりです。
    • false(デフォルト): 認証情報マネージャーの呼び出しがユーザーによる明示的な操作によってトリガーされた場合は、この値を使用します。
    • true: アプリを初めて開くときなど、Credential Manager が機会的に呼び出される場合は、この値を使用します。
      値を true に設定した場合に、すぐに利用可能な認証情報がないと、Credential Manager は UI を表示せず、リクエストはすぐに失敗し、get リクエストでは NoCredentialException、create リクエストでは CreateCredentialNoCreateOptionException が返されます。
  • origin: このフィールドは Android アプリ用に自動的に設定されます。origin を設定する必要があるブラウザや同様の特権アプリについては、特権アプリで代理で認証情報マネージャーの呼び出しを行うをご覧ください。
  • isConditional: これは省略可能なフィールドで、デフォルトは false です。これを true に設定すると、ユーザーがパスキーを持っていない場合、保存されたパスワードで次回ログインしたときに、ユーザーの代わりにパスキーが自動的に作成されます。パスキーはユーザーの認証情報プロバイダに保存されます。条件付き作成機能を使用するには、androidx.credentials最新バージョンが必要です。

createCredential() 関数を呼び出すと、認証情報マネージャーの組み込みボトムシート UI が起動し、ユーザーにパスキーの使用と、保存する認証情報プロバイダとアカウントの選択を求めるメッセージが表示されます。ただし、isConditionaltrue に設定されている場合、ボトムシート UI は表示されず、パスキーが自動的に作成されます。

5. レスポンスを処理する

デバイスの画面ロックを使用してユーザーが認証されると、パスキーが作成され、ユーザーが選択した認証情報プロバイダに保存されます。

createCredential() の呼び出しが成功した後のレスポンスは PublicKeyCredential オブジェクトです。

PublicKeyCredential は次のようになります。

{
  "id": "<identifier>",
  "type": "public-key",
  "rawId": "<identifier>",
  "response": {
    "clientDataJSON": "<ArrayBuffer encoded object with the origin and signed challenge>",
    "attestationObject": "<ArrayBuffer encoded object with the public key and other information.>"
  },
  "authenticatorAttachment": "platform"
}

クライアント アプリでオブジェクトをシリアル化し、アプリサーバーに送信します。

次のスニペットに示すように、失敗を処理するコードを追加します。

fun handleFailure(e: CreateCredentialException) {
    when (e) {
        is CreatePublicKeyCredentialDomException -> {
            // Handle the passkey DOM errors thrown according to the
            // WebAuthn spec.
        }
        is CreateCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to register the credential.
        }
        is CreateCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is CreateCredentialProviderConfigurationException -> {
            // Your app is missing the provider configuration dependency.
            // Most likely, you're missing the
            // "credentials-play-services-auth" module.
        }
        is CreateCredentialCustomException -> {
            // You have encountered an error from a 3rd-party SDK. If you
            // make the API call with a request object that's a subclass of
            // CreateCustomCredentialRequest using a 3rd-party SDK, then you
            // should check for any custom exception type constants within
            // that SDK to match with e.type. Otherwise, drop or log the
            // exception.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java.name}")
    }
}

6. アプリサーバーで公開鍵を確認して保存する

アプリサーバーで、公開鍵認証情報を検証し、公開鍵を保存する必要があります。

公開鍵認証情報のオリジンを検証するには、承認済みアプリの許可リストと照合します。キーのオリジンが認識できない場合は、キーを拒否します。

アプリの SHA 256 フィンガープリントを取得するには:

  1. ターミナルで次のコマンドを実行して、リリースアプリの署名証明書を出力します。

    keytool -list -keystore <path-to-apk-signing-keystore>
    

    レスポンスで、署名証明書の SHA 256 フィンガープリント(Certificate fingerprints block : SHA256)を特定します。

  2. SHA256 フィンガープリントを base64url エンコードでエンコードします。次の Python の例は、フィンガープリントを適切にエンコードする方法を示しています。

    import binascii
    import base64
    fingerprint = '<SHA256 finerprint>' # your app's SHA256 fingerprint
    print(base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))
    
  3. 前の手順の出力の先頭に android:apk-key-hash: を追加して、次のような結果を取得します。

    android:apk-key-hash:<encoded SHA 256 fingerprint>
    

    結果は、アプリサーバーで許可されている送信元と一致する必要があります。複数の署名証明書(デバッグとリリース用の証明書や複数のアプリなど)がある場合は、このプロセスを繰り返して、すべての送信元をアプリサーバーで有効として受け入れます。

7. ユーザーに通知する

パスキーが正常に作成されたら、ユーザーにパスキーについて通知し、認証情報プロバイダ アプリまたはアプリの設定内でパスキーを管理できることを伝えます。カスタム ダイアログ、通知、スナックバーを使用してユーザーに通知します。悪意のあるエンティティによる予期しないパスキーの作成には、即時のセキュリティ アラートが必要になるため、アプリ内メソッドをメールなどの外部通信で補完することを検討してください。

ユーザー エクスペリエンスの向上

Credential Manager を使用した登録を実装する際にユーザー エクスペリエンスを向上させるには、認証情報の復元と自動入力ダイアログの抑制の機能を追加することを検討してください。

新しいデバイスで認証情報を復元する機能を追加

ユーザーが新しいデバイスでアカウントにシームレスにログインできるようにするには、認証情報の復元機能を実装します。BackupAgent で復元認証情報を追加すると、ユーザーが新しいデバイスで復元されたアプリを開いたときにログインし、すぐにアプリを使用できるようになります。

認証情報フィールドの自動入力を抑制する(省略可)

ユーザーが認証に Credential Manager のボトムシート UI を使用することが想定されるアプリの画面では、ユーザー名とパスワードのフィールドに isCredential 属性を追加します。これにより、自動入力ダイアログ(FillDialogSaveDialog)が Credential Manager のボトムシート UI と重なるのを防ぎます。

isCredential 属性は Android 14 以降でサポートされています。

次の例は、アプリの関連するビューの関連するユーザー名フィールドとパスワード フィールドに isCredential 属性を追加する方法を示しています。

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:isCredential="true" />

次のステップ