Nutzer mit Credential Manager anmelden

Der Anmeldedatenmanager ist eine Jetpack API, die mehrere Anmeldemethoden wie Nutzername und Passwort, Passkeys und Lösungen für die föderierte Anmeldung (z. B. „Über Google anmelden)“ in einer einzigen API unterstützt. Dies vereinfacht die Integration für Entwickler.

Außerdem vereinheitlicht der Credential Manager für Nutzer die Anmeldeoberfläche über verschiedene Authentifizierungsmethoden hinweg, wodurch es für Nutzer einfacher und klarer wird, sich bei Apps anzumelden, unabhängig von der gewählten Methode.

Auf dieser Seite werden das Konzept von Passkeys und die Schritte zur Implementierung clientseitiger Unterstützung für Authentifizierungslösungen, einschließlich Passkeys, mithilfe der Credential Manager API erläutert. Außerdem gibt es eine separate FAQ-Seite mit Antworten auf ausführlichere, spezifische Fragen.

Ihr Feedback ist sehr wichtig zur Verbesserung der Credential Manager API. Teilen Sie uns über den folgenden Link alle Probleme oder Vorschläge zur Verbesserung der API mit:

Feedback geben

Passkeys

Passkeys sind ein sicherer und einfacherer Ersatz für Passwörter. Mit Passkeys können sich Nutzer mit einem biometrischen Sensor (z. B. Fingerabdruck oder Gesichtserkennung), PIN oder Muster in Apps und Websites anmelden. Dies ermöglicht eine nahtlose Anmeldung, ohne dass sich Nutzer Nutzernamen oder Passwörter merken müssen.

Passkeys basieren auf WebAuthn (Web Authentication), einen gemeinsam von der FIDO Alliance und dem World Wide Web Consortium (W3C) entwickelten Standard. WebAuthn verwendet Public-Key-Kryptografie zur Authentifizierung des Nutzers. Die Website oder App, bei der sich der Nutzer anmeldet, kann den öffentlichen Schlüssel sehen und speichern, aber nie den privaten Schlüssel. Der private Schlüssel wird geheim und sicher aufbewahrt. Da der Schlüssel eindeutig und an die Website oder App gebunden ist, sind Passkeys nicht Phishing-verdächtig, was die Sicherheit zusätzlich erhöht.

Mit dem Anmeldedatenmanager können Nutzer Passkeys erstellen und im Google Passwortmanager speichern.

Voraussetzungen

Führen Sie die Schritte in diesem Abschnitt aus, um den Anmeldedaten-Manager zu verwenden.

Aktuelle Plattformversion verwenden

Der Anmeldedaten-Manager wird unter Android 4.4 (API-Level 19) und höher unterstützt.

Abhängigkeiten zur Anwendung hinzufügen

Fügen Sie dem Build-Skript Ihres App-Moduls die folgenden Abhängigkeiten hinzu:

Kotlin

dependencies {
    implementation("androidx.credentials:credentials:1.3.0-alpha03")

    // optional - needed for credentials support from play services, for devices running
    // Android 13 and below.
    implementation("androidx.credentials:credentials-play-services-auth:1.3.0-alpha03")
}

Groovig

dependencies {
    implementation "androidx.credentials:credentials:1.3.0-alpha03"

    // optional - needed for credentials support from play services, for devices running
    // Android 13 and below.
    implementation "androidx.credentials:credentials-play-services-auth:1.3.0-alpha03"
}

Klassen in ProGuard-Datei beibehalten

Fügen Sie der Datei proguard-rules.pro Ihres Moduls die folgenden Anweisungen hinzu:

-if class androidx.credentials.CredentialManager
-keep class androidx.credentials.playservices.** {
  *;
}

Weitere Informationen zum Verkleinern, Verschleiern und Optimieren Ihrer App

Unterstützung für Digital Asset Links hinzufügen

Damit deine Android-App Passkey-Unterstützung bietet, musst du sie mit einer Website verknüpfen, deren Inhaber deine App ist. So kannst du diese Verknüpfung deklarieren:

  1. Erstellen Sie eine Digital Asset Links-JSON-Datei. Um beispielsweise zu deklarieren, dass die Website https://signin.example.com und eine Android-App mit dem Paketnamen com.example Anmeldedaten freigeben können, erstellen Sie eine Datei mit dem Namen assetlinks.json und folgendem Inhalt:

    [
      {
        "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
          ]
        }
      }
    ]
    

    Das Feld relation ist ein Array aus einem oder mehreren Strings, die die deklarierte Beziehung beschreiben. Wenn du deklarieren möchtest, dass Apps und Websites dieselben Anmeldedaten verwenden, gib die Beziehungen als delegate_permission/handle_all_urls und delegate_permission/common.get_login_creds an.

    Das Feld target ist ein Objekt, das den Inhalt angibt, für den die Deklaration gilt. Die folgenden Felder identifizieren eine Website:

    namespace web
    site

    Die URL der Website im Format https://domain[:optional_port], z. B. https://www.example.com.

    domain muss voll qualifiziert sein und optional_port muss weggelassen werden, wenn Port 443 für HTTPS verwendet wird.

    Ein site-Ziel kann nur eine Stammdomain sein. Eine Anwendungsverknüpfung kann nicht auf ein bestimmtes Unterverzeichnis beschränkt werden. Die URL muss keinen Pfad, z. B. einen abschließenden Schrägstrich, enthalten.

    Subdomains werden nicht als übereinstimmend angesehen. Wenn Sie also domain als www.example.com angeben, ist die Domain www.counter.example.com nicht mit Ihrer Anwendung verknüpft.

    Die folgenden Felder identifizieren eine Android-App:

    namespace android_app
    package_name Der im Manifest der App deklarierte Paketname. Beispiel: com.example.android
    sha256_cert_fingerprints Die SHA256-Fingerabdrücke des Signaturzertifikats Ihrer App.
  2. Hoste die Digital Assets Link-JSON-Datei unter dem folgenden Speicherort in der Anmeldedomain:

    https://domain[:optional_port]/.well-known/assetlinks.json
    

    Wenn Ihre Anmeldedomain beispielsweise signin.example.com lautet, hosten Sie die JSON-Datei unter https://signin.example.com/.well-known/assetlinks.json.

    Der MIME-Typ für die Digital Assets Link-Datei muss JSON sein. Achte darauf, dass der Server in der Antwort einen Content-Type: application/json-Header sendet.

  3. Dein Host muss Google erlauben, deine Digital Asset Link-Datei abzurufen. Wenn du eine robots.txt-Datei hast, muss der Googlebot-Agent /.well-known/assetlinks.json abrufen können. Auf den meisten Websites kann jeder automatische Agent Dateien im Pfad /.well-known/ abrufen, damit andere Dienste auf die Metadaten in diesen Dateien zugreifen können:

    User-agent: *
    Allow: /.well-known/
    
  4. Fügen Sie der Manifestdatei unter <application> die folgende Zeile hinzu:

    <meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
    
  5. Wenn du die Passwortanmeldung über den Anmeldedaten-Manager verwendest, führe diesen Schritt aus, um die Verknüpfung digitaler Assets im Manifest zu konfigurieren. Dieser Schritt ist nicht erforderlich, wenn Sie nur Passkeys verwenden.

    Deklarieren Sie die Verknüpfung in der Android-App. Fügen Sie ein Objekt hinzu, das die zu ladenden assetlinks.json-Dateien angibt. Apostrophe und Anführungszeichen, die Sie in einem String verwenden, müssen mit Escapezeichen versehen werden. Beispiele:

    <string name="asset_statements" translatable="false">
    [{
      \"include\": \"https://signin.example.com/.well-known/assetlinks.json\"
    }]
    </string>
    
    > GET /.well-known/assetlinks.json HTTP/1.1
    > User-Agent: curl/7.35.0
    > Host: signin.example.com
    
    < HTTP/1.1 200 OK
    < Content-Type: application/json
    

Anmeldedaten-Manager konfigurieren

Zum Konfigurieren und Initialisieren eines CredentialManager-Objekts fügen Sie eine Logik ähnlich der folgenden hinzu:

Kotlin

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

Java

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

Felder für Anmeldedaten angeben

Unter Android 14 und höher kann das Attribut isCredential zur Angabe von Anmeldedatenfeldern wie Nutzername oder Passwort verwendet werden. Dieses Attribut gibt an, dass diese Ansicht ein Anmeldedatenfeld ist, das mit dem Anmeldedaten-Manager und Drittanbietern von Anmeldedaten verwendet werden kann. Gleichzeitig können Autofill-Dienste bessere Autofill-Vorschläge bereitstellen. Wenn die App die Credential Manager API verwendet, wird die Ansicht am unteren Rand des Anmeldedaten-Managers mit den verfügbaren Anmeldedaten angezeigt. Es muss dann nicht mehr das Autofill-Dialogfeld für die Ausfüllung des Nutzernamens oder Passworts angezeigt werden.

Wenn Sie das Attribut isCredential verwenden möchten, fügen Sie es den entsprechenden Datenansichten hinzu:

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

Nutzer anmelden

So rufen Sie alle Passkey- und Passwortoptionen ab, die dem Konto des Nutzers zugeordnet sind:

  1. Initialisieren Sie die Optionen für die Passwort- und Passkey-Authentifizierung:

    Kotlin

    // Retrieves the user's saved password for your app from their
    // password provider.
    val getPasswordOption = GetPasswordOption()
    
    // Get passkey from the user's public key credential provider.
    val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
        requestJson = requestJson
    )

    Java

    // Retrieves the user's saved password for your app from their
    // password provider.
    GetPasswordOption getPasswordOption = new GetPasswordOption();
    
    // Get passkey from the user's public key credential provider.
    GetPublicKeyCredentialOption getPublicKeyCredentialOption =
            new GetPublicKeyCredentialOption(requestJson);
  2. Verwenden Sie die im vorherigen Schritt abgerufenen Optionen, um die Anmeldeanfrage zu erstellen.

    Kotlin

    val getCredRequest = GetCredentialRequest(
        listOf(getPasswordOption, getPublicKeyCredentialOption)
    )

    Java

    GetCredentialRequest getCredRequest = new GetCredentialRequest.Builder()
        .addCredentialOption(getPasswordOption)
        .addCredentialOption(getPublicKeyCredentialOption)
        .build();
  3. Starten Sie den Anmeldevorgang:

    Kotlin

    coroutineScope.launch {
        try {
            val result = credentialManager.getCredential(
                // Use an activity-based context to avoid undefined system UI
                // launching behavior.
                context = activityContext,
                request = getCredRequest
            )
            handleSignIn(result)
        } catch (e : GetCredentialException) {
            handleFailure(e)
        }
    }
    
    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")
            }
        }
    }

    Java

    credentialManager.getCredentialAsync(
        // Use activity based context to avoid undefined
        // system UI launching behavior
        activity,
        getCredRequest,
        cancellationSignal,
        <executor>,
        new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
            @Override
            public void onResult(GetCredentialResponse result) {
                handleSignIn(result);
            }
    
            @Override
            public void onError(GetCredentialException e) {
                handleFailure(e);
            }
        }
    );
    
    public void handleSignIn(GetCredentialResponse result) {
        // Handle the successfully returned credential.
        Credential credential = result.getCredential();
        if (credential instanceof PublicKeyCredential) {
            String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson();
            // Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate
        } else if (credential instanceof PasswordCredential) {
            String username = ((PasswordCredential) credential).getId();
            String password = ((PasswordCredential) credential).getPassword();
            // Use id and password to send to your server to validate and authenticate
        } else if (credential instanceof CustomCredential) {
            if (ExampleCustomCredential.TYPE.equals(credential.getType())) {
                try {
                    ExampleCustomCredential customCred = ExampleCustomCredential.createFrom(customCredential.getData());
                    // Extract the required credentials and complete the
                    // authentication as per the federated sign in or any external
                    // sign in library flow
                } catch (ExampleCustomCredential.ExampleCustomCredentialParsingException e) {
                    // 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");
        }
    }

Das folgende Beispiel zeigt, wie die JSON-Anfrage formatiert wird, wenn Sie einen Passkey erhalten:

{
  "challenge": "T1xCsnxM2DNL2KdK5CLa6fMhD7OBqho6syzInk_n-Uo",
  "allowCredentials": [],
  "timeout": 1800000,
  "userVerification": "required",
  "rpId": "credential-manager-app-test.glitch.me"
}

Das folgende Beispiel zeigt, wie eine JSON-Antwort nach Erhalt der Anmeldedaten eines öffentlichen Schlüssels aussehen könnte:

{
  "id": "KEDetxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "KEDetxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiVDF4Q3NueE0yRE5MMktkSzVDTGE2Zk1oRDdPQnFobzZzeXpJbmtfbi1VbyIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "authenticatorData": "j5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGQdAAAAAA",
    "signature": "MEUCIQCO1Cm4SA2xiG5FdKDHCJorueiS04wCsqHhiRDbbgITYAIgMKMFirgC2SSFmxrh7z9PzUqr0bK1HZ6Zn8vZVhETnyQ",
    "userHandle": "2HzoHm_hY0CjuEESY9tY6-3SdjmNHOoNqaPDcZGzsr0"
  }
}

Ausnahmen behandeln, wenn keine Anmeldedaten verfügbar sind

Es kann vorkommen, dass der Nutzer keine verfügbaren Anmeldedaten hat oder der Verwendung verfügbarer Anmeldedaten keine Einwilligung erteilt. Wenn getCredential() aufgerufen wird und keine Anmeldedaten gefunden werden, wird ein NoCredentialException zurückgegeben. In diesem Fall sollte der Code die NoCredentialException-Instanzen verarbeiten können.

Kotlin

try {
  val credential = credentialManager.getCredential(credentialRequest)
} catch (e: NoCredentialException) {
  Log.e("CredentialManager", "No credential available", e)
}

Java

try {
  Credential credential = credentialManager.getCredential(credentialRequest);
} catch (NoCredentialException e) {
  Log.e("CredentialManager", "No credential available", e);
}

Unter Android 14 oder höher können Sie die Latenz beim Anzeigen der Kontoauswahl reduzieren. Dazu verwenden Sie die Methode prepareGetCredential() vor dem Aufruf von getCredential().

Kotlin

val response = credentialManager.prepareGetCredential(
  GetCredentialRequest(
    listOf(
      <getPublicKeyCredentialOption>,
      <getPasswordOption>
    )
  )
}

Java

GetCredentialResponse response = credentialManager.prepareGetCredential(
  new GetCredentialRequest(
    Arrays.asList(
      new PublicKeyCredentialOption(),
      new PasswordOption()
    )
  )
);

Die Methode prepareGetCredential() ruft keine UI-Elemente auf. Es hilft Ihnen nur bei der Vorbereitung, sodass Sie später den verbleibenden Vorgang zum Abrufen von Anmeldedaten (mit UIs) über die getCredential() API starten können.

Die im Cache gespeicherten Daten werden in einem PrepareGetCredentialResponse-Objekt zurückgegeben. Sind bereits Anmeldedaten vorhanden, werden die Ergebnisse im Cache gespeichert und Sie können später die verbleibende getCredential() API starten, um die Kontoauswahl mit den im Cache gespeicherten Daten aufzurufen.

Registrierungsabläufe

Sie können einen Nutzer mit einem Passkey oder einem Passwort für die Authentifizierung registrieren.

Passkey erstellen

Damit Nutzer einen Passkey registrieren und für die erneute Authentifizierung verwenden können, müssen Sie die Anmeldedaten eines Nutzers mit einem CreatePublicKeyCredentialRequest-Objekt registrieren.

Kotlin

fun createPasskey(requestJson: String, preferImmediatelyAvailableCredentials: Boolean) {
    val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
        // Contains the request in JSON format. Uses the standard WebAuthn
        // web JSON spec.
        requestJson = requestJson,
        // Defines whether you prefer to use only immediately available
        // credentials, not hybrid credentials, to fulfill this request.
        // This value is false by default.
        preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
    )

    // Execute CreateCredentialRequest asynchronously to register credentials
    // for a user account. Handle success and failure cases with the result and
    // exceptions, respectively.
    coroutineScope.launch {
        try {
            val result = credentialManager.createCredential(
                // Use an activity-based context to avoid undefined system
                // UI launching behavior
                context = activityContext,
                request = createPublicKeyCredentialRequest,
            )
            handlePasskeyRegistrationResult(result)
        } catch (e : CreateCredentialException){
            handleFailure(e)
        }
    }
}

fun handleFailure(e: CreateCredentialException) {
    when (e) {
        is CreatePublicKeyCredentialDomException -> {
            // Handle the passkey DOM errors thrown according to the
            // WebAuthn spec.
            handlePasskeyError(e.domError)
        }
        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 CreateCredentialUnknownException -> ...
        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}")
    }
}

Java

public void createPasskey(String requestJson, boolean preferImmediatelyAvailableCredentials) {
    CreatePublicKeyCredentialRequest createPublicKeyCredentialRequest =
            // `requestJson` contains the request in JSON format. Uses the standard
            // WebAuthn web JSON spec.
            // `preferImmediatelyAvailableCredentials` defines whether you prefer
            // to only use immediately available credentials, not  hybrid credentials,
            // to fulfill this request. This value is false by default.
            new CreatePublicKeyCredentialRequest(
                requestJson, preferImmediatelyAvailableCredentials);

    // Execute CreateCredentialRequest asynchronously to register credentials
    // for a user account. Handle success and failure cases with the result and
    // exceptions, respectively.
    credentialManager.createCredentialAsync(
        // Use an activity-based context to avoid undefined system
        // UI launching behavior
        requireActivity(),
        createPublicKeyCredentialRequest,
        cancellationSignal,
        executor,
        new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
            @Override
            public void onResult(CreateCredentialResponse result) {
                handleSuccessfulCreatePasskeyResult(result);
            }

            @Override
            public void onError(CreateCredentialException e) {
                if (e instanceof CreatePublicKeyCredentialDomException) {
                    // Handle the passkey DOM errors thrown according to the
                    // WebAuthn spec.
                    handlePasskeyError(((CreatePublicKeyCredentialDomException)e).getDomError());
                } else if (e instanceof CreateCredentialCancellationException) {
                    // The user intentionally canceled the operation and chose not
                    // to register the credential.
                } else if (e instanceof CreateCredentialInterruptedException) {
                    // Retry-able error. Consider retrying the call.
                } else if (e instanceof CreateCredentialProviderConfigurationException) {
                    // Your app is missing the provider configuration dependency.
                    // Most likely, you're missing the
                    // "credentials-play-services-auth" module.
                } else if (e instanceof CreateCredentialUnknownException) {
                } else if (e instanceof 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.getClass().getName());
                }
            }
        }
    );
}

JSON-Anfrage formatieren

Nachdem du einen Passkey erstellt hast, musst du ihn dem Konto eines Nutzers zuordnen und den öffentlichen Schlüssel des Passkeys auf deinem Server speichern. Das folgende Codebeispiel zeigt, wie die JSON-Anfrage beim Erstellen eines Passkeys formatiert wird.

In diesem Blogpost zur nahtlosen Authentifizierung für Ihre Apps erfahren Sie, wie Sie Ihre JSON-Anfrage formatieren, wenn Sie Passkeys erstellen und sich mit Passkeys authentifizieren. Außerdem wird erklärt, warum Passwörter keine effektive Authentifizierungslösung sind, wie Sie vorhandene biometrische Anmeldedaten nutzen, wie Sie Ihre App mit einer Ihrer Websites verknüpfen, wie Sie Passkeys erstellen und wie Sie sich mit Passkeys authentifizieren.

{
  "challenge": "abc123",
  "rp": {
    "name": "Credential Manager example",
    "id": "credential-manager-test.example.com"
  },
  "user": {
    "id": "def456",
    "name": "helloandroid@gmail.com",
    "displayName": "helloandroid@gmail.com"
  },
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -7
    },
    {
      "type": "public-key",
      "alg": -257
    }
  ],
  "timeout": 1800000,
  "attestation": "none",
  "excludeCredentials": [
    {"id": "ghi789", "type": "public-key"},
    {"id": "jkl012", "type": "public-key"}
  ],
  "authenticatorSelection": {
    "authenticatorAttachment": "platform",
    "requireResidentKey": true,
    "residentKey": "required",
    "userVerification": "required"
  }
}

Werte für authentifiziererAnhang festlegen

Der Parameter authenticatorAttachment kann nur beim Erstellen der Anmeldedaten festgelegt werden. Sie können platform, cross-platform oder keinen Wert angeben. In den meisten Fällen wird kein Wert empfohlen.

  • platform: Wenn du das aktuelle Gerät des Nutzers registrieren oder einen Passwortnutzer auffordern möchtest, nach der Anmeldung ein Upgrade auf Passkeys durchzuführen, setze authenticatorAttachment auf platform.
  • cross-platform: Dieser Wert wird häufig beim Registrieren von Multi-Faktor-Anmeldedaten verwendet und nicht in einem Passkey-Kontext.
  • Kein Wert: Damit Nutzer flexibel Passkeys auf ihren bevorzugten Geräten erstellen können (z. B. in den Kontoeinstellungen), sollte der Parameter authenticatorAttachment nicht angegeben werden, wenn ein Nutzer einen Passkey hinzufügt. In den meisten Fällen ist es die beste Option, den Parameter nicht angegeben zu lassen.

Erstellen doppelter Passkeys verhindern

Listen Sie Anmeldedaten-IDs im optionalen Array excludeCredentials auf, um zu verhindern, dass ein neuer Passkey erstellt wird, wenn bereits ein Passkey-Anbieter vorhanden ist.

JSON-Antwort verarbeiten

Das folgende Code-Snippet zeigt eine Beispiel-JSON-Antwort zum Erstellen von Anmeldedaten mit öffentlichem Schlüssel. Weitere Informationen zum Umgang mit den zurückgegebenen Anmeldedaten für öffentliche Schlüssel

{
  "id": "KEDetxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "KEDetxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibmhrUVhmRTU5SmI5N1Z5eU5Ka3ZEaVh1Y01Fdmx0ZHV2Y3JEbUdyT0RIWSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUj5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGRdAAAAAAAAAAAAAAAAAAAAAAAAAAAAEChA3rcWXFH4p4VYumWuZ2WlAQIDJiABIVgg4RqZaJyaC24Pf4tT-8ONIZ5_Elddf3dNotGOx81jj3siWCAWXS6Lz70hvC2g8hwoLllOwlsbYatNkO2uYFO-eJID6A"
  }
}

Ursprung aus Kundendaten-JSON prüfen

Der origin steht für die Anwendung oder Website, von der eine Anfrage stammt, und wird von Passkeys zum Schutz vor Phishingangriffen verwendet. Der Server Ihrer App muss die Herkunft der Clientdaten anhand einer Zulassungsliste genehmigter Apps und Websites prüfen. Wenn der Server eine Anfrage von einer App oder Website erhält, die nicht erkannt wurde, sollte die Anfrage abgelehnt werden.

Im Web-Fall entspricht origin dem Ursprung derselben Website, an dem die Anmeldedaten angemeldet waren. Beispiel: Wenn die URL https://www.example.com:8443/store?category=shoes#athletic lautet, ist der origin-Wert https://www.example.com:8443.

Bei Android-Apps legt der User-Agent origin automatisch auf die Signatur der aufrufenden App fest. Diese Signatur sollte auf deinem Server als Übereinstimmung geprüft werden, um den Aufrufer der Passkey API zu validieren. Das Android-origin ist ein URI, der vom SHA-256-Hash des APK-Signaturzertifikats abgeleitet ist, z. B.:

android:apk-key-hash:<sha256_hash-of-apk-signing-cert>

Die SHA-256-Hashes der Signaturzertifikate aus einem Schlüsselspeicher können mit dem folgenden Terminalbefehl abgerufen werden:

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

Die SHA-256-Hashes liegen in einem durch Doppelpunkt getrennten Hexadezimalformat vor (91:F7:CB:F9:D6:81…) und die origin-Werte von Android sind base64url-codiert. Dieses Python-Beispiel zeigt, wie das Hash-Format in ein kompatibles, durch Doppelpunkt getrenntes Hexadezimalformat konvertiert wird:

import binascii
import base64
fingerprint = '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5'
print("android:apk-key-hash:" + base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))

Ersetzen Sie den Wert von fingerprint durch Ihren eigenen Wert. Hier ist ein Beispielergebnis:

android:apk-key-hash:kffL-daBUxvHpY-4M8yhTavt5QnFEI2LsexohxrGPYU

Sie können diesen String dann auf Ihrem Server als zulässigen Ursprung abgleichen. Wenn Sie mehrere Signaturzertifikate haben, z. B. Zertifikate für die Fehlerbehebung und die Freigabe, oder mehrere Apps, wiederholen Sie den Vorgang und akzeptieren alle diese Ursprünge auf dem Server als gültig.

Passwort eines Nutzers speichern

Wenn der Nutzer einen Nutzernamen und ein Passwort für einen Authentifizierungsvorgang in Ihrer Anwendung angibt, können Sie Anmeldedaten registrieren, die zur Authentifizierung des Nutzers verwendet werden können. Erstellen Sie dazu ein CreatePasswordRequest-Objekt:

Kotlin

fun registerPassword(username: String, password: String) {
    // Initialize a CreatePasswordRequest object.
    val createPasswordRequest =
            CreatePasswordRequest(id = username, password = password)

    // Create credential and handle result.
    coroutineScope.launch {
        try {
            val result =
                credentialManager.createCredential(
                    // Use an activity based context to avoid undefined
                    // system UI launching behavior.
                    activityContext,
                    createPasswordRequest
                  )
            handleRegisterPasswordResult(result)
        } catch (e: CreateCredentialException) {
            handleFailure(e)
        }
    }
}

Java

void registerPassword(String username, String password) {
    // Initialize a CreatePasswordRequest object.
    CreatePasswordRequest createPasswordRequest =
        new CreatePasswordRequest(username, password);

    // Register the username and password.
    credentialManager.createCredentialAsync(
        // Use an activity-based context to avoid undefined
        // system UI launching behavior
        requireActivity(),
        createPasswordRequest,
        cancellationSignal,
        executor,
        new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
            @Override
            public void onResult(CreateCredentialResponse result) {
                handleResult(result);
            }

            @Override
            public void onError(CreateCredentialException e) {
                handleFailure(e);
            }
        }
    );
}

Wiederherstellung von Anmeldedaten unterstützen

Wenn ein Nutzer keinen Zugriff mehr auf ein Gerät hat, auf dem er seine Anmeldedaten gespeichert hat, muss er möglicherweise aus einer sicheren Onlinesicherung wiederhergestellt werden. Weitere Informationen dazu, wie du die Wiederherstellung der Anmeldedaten unterstützen kannst, findest du im Abschnitt „Wiederherstellen des Zugriffs oder Hinzufügen neuer Geräte“ in diesem Blogpost: Sicherheit von Passkeys im Google Passwortmanager.

Unterstützung für Tools zur Passwortverwaltung mit bekannten URLs mit Passkey-Endpunkten hinzufügen

Für eine nahtlose Integration und zukünftige Kompatibilität mit Tools zur Passwort- und Anmeldedatenverwaltung empfehlen wir, Unterstützung für Passkey-Endpunkte und bekannte URLs hinzuzufügen. Dies ist ein offenes Protokoll für Parteien, mit denen sie ihre Unterstützung für Passkeys offiziell bewerben und direkte Links zur Registrierung und Verwaltung von Passkeys bereitstellen können.

  1. Für eine vertrauende Partei bei https://example.com, die eine Website sowie Android- und iOS-Apps hat, ist die bekannte URL https://example.com/.well-known/passkey-endpoints.
  2. Wenn die URL abgefragt wird, sollte die Antwort das folgende Schema verwenden

    {
      "enroll": "https://example.com/account/manage/passkeys/create"
      "manage": "https://example.com/account/manage/passkeys"
    }
    
  3. Wenn dieser Link direkt in Ihrer App und nicht im Web geöffnet werden soll, verwenden Sie Android-App-Links.

  4. Weitere Informationen finden Sie in der Erläuterung der bekannten URL für Passkey-Endpunkte auf GitHub.

Häufige Fehler beheben

Die folgende Tabelle enthält einige häufige Fehlercodes, -beschreibungen sowie Informationen zu den Ursachen:

Fehlercode und -beschreibung Ursache
Fehler beim Starten der Anmeldung: 16: Der Anrufer wurde aufgrund zu vieler abgebrochener Anmeldeaufforderungen vorübergehend blockiert.

Wenn diese 24-stündige Wartezeit während der Entwicklung erreicht wird, können Sie sie zurücksetzen, indem Sie den App-Speicher der Google Play-Dienste löschen.

Wenn Sie diese Wartezeit auf einem Testgerät oder Emulator ein- oder ausschalten möchten, rufen Sie die Telefon-App auf und geben Sie den folgenden Code ein: *#*#66382723#*#*. Die Telefon App löscht alle Eingaben und wird möglicherweise geschlossen, es wird jedoch keine Bestätigungsmeldung angezeigt.

Fehler beim Starten der Anmeldung: 8: Unbekannter interner Fehler.
  1. Das Gerät ist nicht richtig mit dem Google-Konto eingerichtet.
  2. Der Passkey-JSON wird nicht richtig erstellt.
CreatePublicKeyCredentialDomException: Die eingehende Anfrage kann nicht validiert werden. Die Paket-ID der App ist nicht auf Ihrem Server registriert. Prüfen Sie dies in Ihrer serverseitigen Integration.
CreateCredentialUnknownException: Beim Speichern des Passworts, Antwort auf Fehler beim Suchen nach einem Tippen 16: Passwortspeicherung wird übersprungen, da der Nutzer wahrscheinlich mit Android Autofill aufgefordert wird Dieser Fehler tritt nur unter Android 13 und niedriger und nur dann auf, wenn Google der AutoFill-Anbieter ist. In diesem Fall wird Nutzern eine Aufforderung zum Speichern von Autofill angezeigt und das Passwort wird im Google Passwortmanager gespeichert. Die mit „Autofill mit Google“ gespeicherten Anmeldedaten werden bidirektional mit der Credential Manager API geteilt. Dieser Fehler kann daher ignoriert werden.

Weitere Informationen

Weitere Informationen zur Credential Manager API und zu Passkeys finden Sie in den folgenden Ressourcen: