パスキーでログインする

このガイドでは、認証にパスキーを使用する実装について説明します。ユーザーがパスキーでログインできるようにするには、パスキーを作成するの手順も完了する必要があります。

パスキーで認証するには、まず アプリサーバーから公開鍵を取得するために必要なオプションを取得し、次に Credential Manager API を呼び出して公開鍵を取得する必要があります。次に、ログイン レスポンスを適切に処理します。

概要

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

ユーザーのアカウントに関連付けられているすべてのパスキーとパスワードのオプションを取得する手順は次のとおりです。

  1. サーバーから認証情報リクエスト オプションを取得する: パスキー ログイン プロセスを開始するために、アプリから認証サーバーにリクエストを行います。サーバーから、公開鍵認証情報を取得するために必要なオプションと一意のチャレンジを送信します。
  2. 公開鍵認証情報を取得するために必要なオブジェクトを作成する: サーバーから送信されたオプションを GetPublicKeyCredentialOption オブジェクトでラップします。
  3. 省略可)getCredential を準備する: Android 14 以降では、getCredential() を呼び出す前に prepareGetCredential() メソッドを使用してアカウント セレクタを表示することで、レイテンシを短縮できます。
  4. ログインフローを開始する: getCredential() メソッドを呼び出してユーザーをログインさせます。
  5. レスポンスを処理する: 可能な認証情報のレスポンスをそれぞれ処理します。
  6. 例外を処理する: 例外を適切に処理してください。

1. サーバーから認証情報リクエスト オプションを取得する

公開鍵認証情報を取得するために必要なオプションと、ログイン試行ごとに一意の challenge をサーバーにリクエストします。サーバーサイドの実装について詳しくは、チャレンジを作成する認証情報リクエスト オプションを作成するをご覧ください。

オプションは次のようになります。

{
  "challenge": "<your app challenge>",
  "allowCredentials": [],
  "rpId": "<your app server domain>"
}

フィールドについて詳しくは、パスキーでのログインに関するブログ投稿をご覧ください。

2. 公開鍵認証情報を取得するために必要なオブジェクトを作成する

アプリで、オプションを使用して GetPublicKeyCredentialOption オブジェクトを作成します。次の例では、requestJson はサーバーから送信されたオプションを表します。

// Get password logins from the credential provider on the user's device.
val getPasswordOption = GetPasswordOption()

// Get passkeys from the credential provider on the user's device.
val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
    requestJson = requestJson
)

次に、GetPublicKeyCredentialOptionGetCredentialRequest オブジェクトでラップします。

val credentialRequest = GetCredentialRequest(
    // Include all the sign-in options that your app supports.
    listOf(getPasswordOption, getPublicKeyCredentialOption),
    // Defines whether you prefer to use only immediately available
    // credentials or hybrid credentials.
    preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials
)

3. 省略可: ログイン レイテンシを短縮する

Android 14 以降では、getCredential() を呼び出す前に prepareGetCredential() メソッドを使用して、アカウント セレクタを表示するときのレイテンシを短縮できます。

prepareGetCredential() メソッドは、キャッシュに保存された PrepareGetCredentialResponse オブジェクトを返します。これにより、次のステップの getCredential() メソッドで、キャッシュに保存されたデータを含むアカウント選択ツールを表示できるようになります。

coroutineScope {
    val response = credentialManager.prepareGetCredential(
        GetCredentialRequest(
            listOf(
                // Include all the sign-in options that your app supports
                getPublicKeyCredentialOption, 
                getPasswordOption
            )
        )
    )
}

4. ログインフローを開始します。

getCredential() メソッドを呼び出して、ユーザーにアカウント セレクタを表示します。次のコード スニペットは、ログインフローを開始する方法の参考として使用してください。

coroutineScope {
    try {
        result = credentialManager.getCredential(
            // Use an activity-based context to avoid undefined system UI
            // launching behavior.
            context = activityContext,
            request = credentialRequest
        )
        handleSignIn(result)
    } catch (e: GetCredentialException) {
        // Handle failure
    }
}

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

レスポンスを処理します。レスポンスには、さまざまなタイプの認証情報オブジェクトのいずれかが含まれます。

fun handleSignIn(result: GetCredentialResponse) {
    // Handle the successfully returned credential.
    val credential = result.credential

    when (credential) {
        is PublicKeyCredential -> {
            val responseJson = credential.authenticationResponseJson
            // Share responseJson i.e. a GetCredentialResponse on your server to
            // validate and  authenticate
        }

        is PasswordCredential -> {
            val username = credential.id
            val password = credential.password
            // Use id and password to send to your server to validate
            // and authenticate
        }

        is CustomCredential -> {
            // If you are also using any external sign-in libraries, parse them
            // here with the utility functions provided.
            if (credential.type == ExampleCustomCredential.TYPE) {
                try {
                    val ExampleCustomCredential =
                        ExampleCustomCredential.createFrom(credential.data)
                    // Extract the required credentials and complete the authentication as per
                    // the federated sign in or any external sign in library flow
                } catch (e: ExampleCustomCredential.ExampleCustomCredentialParsingException) {
                    // Unlikely to happen. If it does, you likely need to update the dependency
                    // version of your external sign-in library.
                    Log.e(TAG, "Failed to parse an ExampleCustomCredential", e)
                }
            } else {
                // Catch any unrecognized custom credential type here.
                Log.e(TAG, "Unexpected type of credential")
            }
        }
        else -> {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential")
        }
    }
}

認証から返される PublicKeyCredential は、基本的に次のように構造化された署名付きアサーションです。

{
  "id": "<credential ID>",
  "type": "public-key",
  "rawId": "<raw credential ID>",
  "response": {
    "clientDataJSON": "<signed client data containing challenge>",
    "authenticatorData": "<authenticator metadata>",
    "signature": "<digital signature to be verified>",
    "userHandle": "<user ID from credential registration>"
  }
}

サーバーで、認証情報を検証する必要があります。詳しくは、ユーザーを確認してログインするをご覧ください。

6. 例外を処理する

GetCredentialException のすべてのサブクラス例外を処理する必要があります。各例外の処理方法については、トラブルシューティング ガイドをご覧ください。

coroutineScope {
    try {
        result = credentialManager.getCredential(
            context = activityContext,
            request = credentialRequest
        )
    } catch (e: GetCredentialException) {
        Log.e("CredentialManager", "No credential available", e)
    }
}

次のステップ