1. Hinweis
Die Verwendung von Passkeys anstelle von Passwörtern ist eine gute Möglichkeit für Websites, ihre Nutzerkonten sicherer, einfacher und benutzerfreundlicher zu machen. Mit einem Passkey kann sich ein Nutzer auf einer Website oder in einer App anmelden, indem er die Displaysperre des Geräts verwendet, z. B. einen Fingerabdruck, die Gesichtserkennung oder eine Geräte-PIN. Ein Passkey muss erstellt, einem Nutzerkonto zugeordnet und sein öffentlicher Schlüssel auf einem Server gespeichert werden, bevor sich ein Nutzer damit anmelden kann.
In diesem Codelab wandeln Sie eine einfache formularbasierte Anmeldung mit Nutzername und Passwort in eine Anmeldung um, die Passkeys unterstützt und Folgendes umfasst:
- Eine Schaltfläche, mit der nach der Anmeldung des Nutzers ein Passkey erstellt wird.
- Eine Benutzeroberfläche, auf der eine Liste der registrierten Passkeys angezeigt wird.
- Das vorhandene Anmeldeformular, mit dem sich Nutzer mit einem registrierten Passkey über die Autofill-Funktion anmelden können.
Vorbereitung
- Grundlegende Kenntnisse von JavaScript
- Grundlegendes Verständnis von Passkeys
- Grundlegendes Verständnis der Web Authentication API (WebAuthn)
Lerninhalte
- So erstellen Sie einen Passkey.
- Nutzer mit einem Passkey authentifizieren
- So kann in einem Formular ein Passkey als Anmeldeoption vorgeschlagen werden.
Voraussetzungen
Eine der folgenden Gerätekonfigurationen:
- Google Chrome auf einem Android-Gerät mit Android 9 oder höher, vorzugsweise mit einem biometrischen Sensor.
- Chrome auf einem Windows-Gerät mit Windows 10 oder höher.
- Safari 16 oder höher auf einem iPhone mit iOS 16 oder höher oder auf einem iPad mit iPadOS 16 oder höher.
- Safari 16 oder höher oder Chrome auf einem Apple-Desktopgerät mit macOS Ventura oder höher.
2. Einrichten
In diesem Codelab verwenden Sie den Dienst Glitch, mit dem Sie Client- und Serverseitencode mit JavaScript bearbeiten und direkt über den Browser bereitstellen können.
Projekt öffnen
- Öffnen Sie das Projekt in Glitch.
- Klicken Sie auf Remix, um das Glitch-Projekt zu forken.
- Klicken Sie im Navigationsmenü unten in Glitch auf Preview > Preview in a new window (Vorschau > Vorschau in einem neuen Fenster). In Ihrem Browser wird ein weiterer Tab geöffnet.
Ausgangszustand der Website prüfen
- Geben Sie auf dem Vorschautab einen zufälligen Nutzernamen ein und klicken Sie auf Weiter.
- Geben Sie ein beliebiges Passwort ein und klicken Sie auf Anmelden. Das Passwort wird ignoriert, Sie werden aber trotzdem authentifiziert und gelangen zur Startseite.
- Wenn Sie Ihren Anzeigenamen ändern möchten, tun Sie das. Das ist alles, was Sie im Ausgangszustand tun können.
- Klicken Sie auf Abmelden.
In diesem Status müssen Nutzer bei jeder Anmeldung ein Passwort eingeben. Sie fügen diesem Formular Unterstützung für Passkeys hinzu, damit sich Nutzer mit der Displaysperre des Geräts anmelden können. Den Endzustand können Sie unter https://passkeys-codelab.glitch.me/ ausprobieren.
Weitere Informationen zur Funktionsweise von Passkeys finden Sie unter Wie funktionieren Passkeys?.
3. Möglichkeit zum Erstellen eines Passkeys hinzufügen
Damit sich Nutzer mit einem Passkey authentifizieren können, müssen Sie ihnen die Möglichkeit geben, einen Passkey zu erstellen und zu registrieren und den zugehörigen öffentlichen Schlüssel auf dem Server zu speichern.
Sie möchten die Erstellung eines Passkeys zulassen, nachdem sich der Nutzer mit einem Passwort angemeldet hat, und eine Benutzeroberfläche hinzufügen, über die Nutzer einen Passkey erstellen und eine Liste aller registrierten Passkeys auf der Seite /home
aufrufen können. Im nächsten Abschnitt erstellen Sie eine Funktion, mit der ein Passkey erstellt und registriert wird.
Funktion registerCredential()
erstellen
- Rufen Sie in Glitch die Datei
public/client.js
auf und scrollen Sie zum Ende. - Fügen Sie nach dem entsprechenden Kommentar die folgende
registerCredential()
-Funktion hinzu:
public/client. js
// TODO: Add an ability to create a passkey: Create the registerCredential() function.
export async function registerCredential() {
// TODO: Add an ability to create a passkey: Obtain the challenge and other options from the server endpoint.
// TODO: Add an ability to create a passkey: Create a credential.
// TODO: Add an ability to create a passkey: Register the credential to the server endpoint.
};
Mit dieser Funktion wird ein Passkey auf dem Server erstellt und registriert.
Herausforderung und andere Optionen vom Serverendpunkt abrufen
Bevor ein Passkey erstellt wird, müssen Sie Parameter an WebAuthn vom Server anfordern, einschließlich einer Challenge. WebAuthn ist eine Browser-API, mit der ein Nutzer einen Passkey erstellen und sich mit dem Passkey authentifizieren kann. Glücklicherweise haben Sie in diesem Codelab bereits einen Serverendpunkt, der mit solchen Parametern antwortet.
- Fügen Sie den folgenden Code nach dem entsprechenden Kommentar in den Textkörper der Funktion
registerCredential()
ein, um die Challenge und andere Optionen vom Serverendpunkt abzurufen:
public/client.js
// TODO: Add an ability to create a passkey: Obtain the challenge and other options from the server endpoint.
const options = await _fetch('/auth/registerRequest');
Das folgende Code-Snippet enthält Beispieloptionen, die Sie vom Server erhalten:
{
challenge: *****,
rp: {
id: "example.com",
},
user: {
id: *****,
name: "john78",
displayName: "John",
},
pubKeyCredParams: [{
alg: -7, type: "public-key"
},{
alg: -257, type: "public-key"
}],
excludeCredentials: [{
id: *****,
type: 'public-key',
transports: ['internal', 'hybrid'],
}],
authenticatorSelection: {
authenticatorAttachment: "platform",
requireResidentKey: true,
}
}
Das Protokoll zwischen einem Server und einem Client ist nicht Teil der WebAuthn-Spezifikation. Der Server dieses Codelabs ist jedoch so konzipiert, dass er ein JSON zurückgibt, das dem PublicKeyCredentialCreationOptions
-Dictionary, das an die WebAuthn-navigator.credentials.create()
-API übergeben wird, so ähnlich wie möglich ist.
Die folgende Tabelle ist nicht vollständig, enthält aber die wichtigen Parameter im PublicKeyCredentialCreationOptions
-Dictionary:
Parameter | Textzeilen |
Eine vom Server generierte Challenge in einem | |
Die eindeutige ID eines Nutzers. Dieser Wert muss ein | |
Dieses Feld sollte eine eindeutige Kennung für das Konto enthalten, die für den Nutzer erkennbar ist, z. B. seine E-Mail-Adresse oder sein Nutzername. Sie wird in der Kontoauswahl angezeigt. Wenn Sie einen Nutzernamen verwenden, geben Sie denselben Wert wie bei der Passwortauthentifizierung ein. | |
Dieses Feld ist ein optionaler, nutzerfreundlicher Name für das Konto. Er muss nicht eindeutig sein und kann der vom Nutzer gewählte Name sein. Wenn Ihre Website keinen geeigneten Wert für dieses Feld hat, übergeben Sie einen leeren String. Je nach Browser wird dies möglicherweise in der Kontoauswahl angezeigt. | |
Eine Relying Party-ID (RP) ist eine Domain. Eine Website kann entweder ihre Domain oder ein registrierbares Suffix angeben. Wenn der Ursprung eines RP beispielsweise https://login.beispiel.de:1337 ist, kann die RP-ID entweder | |
In diesem Feld werden die vom RP unterstützten Algorithmen für öffentliche Schlüssel angegeben. Wir empfehlen, diesen Wert auf | |
Stellt eine Liste der bereits registrierten Anmeldedaten-IDs bereit, um eine doppelte Registrierung desselben Geräts zu verhindern. Falls angegeben, sollte das | |
Legen Sie einen | |
Auf einen booleschen Wert | |
Legen Sie den Wert auf |
Anmeldedaten erstellen
- Konvertieren Sie im Funktionskörper von
registerCredential()
nach dem entsprechenden Kommentar einige mit Base64URL codierte Parameter zurück in Binärdaten, insbesondere die Stringsuser.id
undchallenge
sowie Instanzen des Stringsid
, die im ArrayexcludeCredentials
enthalten sind:
public/client.js
// TODO: Add an ability to create a passkey: Create a credential.
// Base64URL decode some values.
options.user.id = base64url.decode(options.user.id);
options.challenge = base64url.decode(options.challenge);
if (options.excludeCredentials) {
for (let cred of options.excludeCredentials) {
cred.id = base64url.decode(cred.id);
}
}
- Legen Sie in der nächsten Zeile
authenticatorSelection.authenticatorAttachment
auf"platform"
undauthenticatorSelection.requireResidentKey
auftrue
fest. Dadurch kann nur ein Plattform-Authenticator (das Gerät selbst) mit einer erkennbaren Anmeldedatenfunktion verwendet werden.
public/client.js
// Use platform authenticator and discoverable credential.
options.authenticatorSelection = {
authenticatorAttachment: 'platform',
requireResidentKey: true
}
- Rufen Sie in der nächsten Zeile die Methode
navigator.credentials.create()
auf, um Anmeldedaten zu erstellen.
public/client.js
// Invoke the WebAuthn create() method.
const cred = await navigator.credentials.create({
publicKey: options,
});
Mit diesem Aufruf versucht der Browser, die Identität des Nutzers mit der Displaysperre des Geräts zu bestätigen.
Anmeldedaten beim Serverendpunkt registrieren
Nachdem der Nutzer seine Identität bestätigt hat, wird ein Passkey erstellt und gespeichert. Die Website erhält ein Anmeldedatenobjekt, das einen öffentlichen Schlüssel enthält, den Sie an den Server senden können, um den Passkey zu registrieren.
Das folgende Code-Snippet enthält ein Beispiel für ein Anmeldedatenobjekt:
{
"id": *****,
"rawId": *****,
"type": "public-key",
"response": {
"clientDataJSON": *****,
"attestationObject": *****,
"transports": ["internal", "hybrid"]
},
"authenticatorAttachment": "platform"
}
Die folgende Tabelle ist nicht vollständig, enthält aber die wichtigen Parameter im PublicKeyCredential
-Objekt:
Parameter | Textzeilen |
Eine Base64URL-codierte ID des erstellten Passkeys. Anhand dieser ID kann der Browser bei der Authentifizierung feststellen, ob sich ein passendes Passkey auf dem Gerät befindet. Dieser Wert muss in der Datenbank im Backend gespeichert werden. | |
Eine | |
Ein | |
Ein | |
Eine Liste der vom Gerät unterstützten Transportmethoden: | |
Gibt |
So senden Sie das Anmeldedatenobjekt an den Server:
- Codieren Sie die binären Parameter der Anmeldedaten als Base64URL, damit sie als String an den Server gesendet werden können:
public/client.js
// TODO: Add an ability to create a passkey: Register the credential to the server endpoint.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// The authenticatorAttachment string in the PublicKeyCredential object is a new addition in WebAuthn L3.
if (cred.authenticatorAttachment) {
credential.authenticatorAttachment = cred.authenticatorAttachment;
}
// Base64URL encode some values.
const clientDataJSON = base64url.encode(cred.response.clientDataJSON);
const attestationObject = base64url.encode(cred.response.attestationObject);
// Obtain transports.
const transports = cred.response.getTransports ? cred.response.getTransports() : [];
credential.response = {
clientDataJSON,
attestationObject,
transports
};
- Senden Sie das Objekt in der nächsten Zeile an den Server:
public/client.js
return await _fetch('/auth/registerResponse', credential);
Wenn Sie das Programm ausführen, gibt der Server HTTP code 200
zurück. Das bedeutet, dass die Anmeldedaten registriert sind.
Jetzt haben Sie die vollständige Funktion registerCredential()
.
Lösungscode für diesen Abschnitt ansehen
public/client.js
// TODO: Add an ability to create a passkey: Create the registerCredential() function.
export async function registerCredential() {
// TODO: Add an ability to create a passkey: Obtain the challenge and other options from server endpoint.
const options = await _fetch('/auth/registerRequest');
// TODO: Add an ability to create a passkey: Create a credential.
// Base64URL decode some values.
options.user.id = base64url.decode(options.user.id);
options.challenge = base64url.decode(options.challenge);
if (options.excludeCredentials) {
for (let cred of options.excludeCredentials) {
cred.id = base64url.decode(cred.id);
}
}
// Use platform authenticator and discoverable credential.
options.authenticatorSelection = {
authenticatorAttachment: 'platform',
requireResidentKey: true
}
// Invoke the WebAuthn create() method.
const cred = await navigator.credentials.create({
publicKey: options,
});
// TODO: Add an ability to create a passkey: Register the credential to the server endpoint.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// The authenticatorAttachment string in the PublicKeyCredential object is a new addition in WebAuthn L3.
if (cred.authenticatorAttachment) {
credential.authenticatorAttachment = cred.authenticatorAttachment;
}
// Base64URL encode some values.
const clientDataJSON = base64url.encode(cred.response.clientDataJSON);
const attestationObject =
base64url.encode(cred.response.attestationObject);
// Obtain transports.
const transports = cred.response.getTransports ?
cred.response.getTransports() : [];
credential.response = {
clientDataJSON,
attestationObject,
transports
};
return await _fetch('/auth/registerResponse', credential);
};
4. UI zum Registrieren und Verwalten von Passkey-Anmeldedaten erstellen
Nachdem die registerCredential()
-Funktion verfügbar ist, benötigen Sie eine Schaltfläche, um sie aufzurufen. Außerdem müssen Sie eine Liste der registrierten Passkeys anzeigen.
Platzhalter-HTML hinzufügen
- Rufen Sie in Glitch die Datei
views/home.html
auf. - Fügen Sie nach dem entsprechenden Kommentar einen UI-Platzhalter ein, in dem eine Schaltfläche zum Registrieren eines Passkeys und eine Liste von Passkeys angezeigt werden:
views/home.html
<!-- TODO: Add an ability to create a passkey: Add placeholder HTML. -->
<section>
<h3 class="mdc-typography mdc-typography--headline6"> Your registered
passkeys:</h3>
<div id="list"></div>
</section>
<p id="message" class="instructions"></p>
<mwc-button id="create-passkey" class="hidden" icon="fingerprint" raised>Create a passkey</mwc-button>
Das Element div#list
ist der Platzhalter für die Liste.
Passkey-Unterstützung prüfen
Damit die Option zum Erstellen eines Passkeys nur Nutzern mit Geräten angezeigt wird, die Passkeys unterstützen, müssen Sie zuerst prüfen, ob WebAuthn verfügbar ist. Wenn ja, müssen Sie die Klasse hidden
entfernen, damit die Schaltfläche Passkey erstellen angezeigt wird.
So prüfen Sie, ob eine Umgebung Passkeys unterstützt:
- Schreiben Sie am Ende der Datei
views/home.html
nach dem entsprechenden Kommentar eine Bedingung, die ausgeführt wird, wennwindow.PublicKeyCredential
,PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable
undPublicKeyCredential.isConditionalMediationAvailable
true
sind.
views/home.html
// TODO: Add an ability to create a passkey: Check for passkey support.
const createPasskey = $('#create-passkey');
// Feature detections
if (window.PublicKeyCredential &&
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&
PublicKeyCredential.isConditionalMediationAvailable) {
- Prüfen Sie im Hauptteil der Bedingung, ob das Gerät einen Passkey erstellen kann, und dann, ob der Passkey beim automatischen Ausfüllen eines Formulars vorgeschlagen werden kann.
views/home.html
try {
const results = await Promise.all([
// Is platform authenticator available in this browser?
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),
// Is conditional UI available in this browser?
PublicKeyCredential.isConditionalMediationAvailable()
]);
- Wenn alle Bedingungen erfüllt sind, wird die Schaltfläche zum Erstellen eines Passkeys angezeigt. Andernfalls wird eine Warnmeldung angezeigt.
views/home.html
if (results.every(r => r === true)) {
// If conditional UI is available, reveal the Create a passkey button.
createPasskey.classList.remove('hidden');
} else {
// If conditional UI isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
} catch (e) {
console.error(e);
}
} else {
// If WebAuthn isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
Registrierte Passkeys in einer Liste rendern
- Definieren Sie eine
renderCredentials()
-Funktion, die registrierte Passkeys vom Server abruft und in einer Liste rendert. Glücklicherweise haben Sie bereits den Serverendpunkt/auth/getKeys
, um registrierte Passkeys für den angemeldeten Nutzer abzurufen.
views/home.html
// TODO: Add an ability to create a passkey: Render registered passkeys in a list.
async function renderCredentials() {
const res = await _fetch('/auth/getKeys');
const list = $('#list');
const creds = html`${res.length > 0 ? html`
<mwc-list>
${res.map(cred => html`
<mwc-list-item>
<div class="list-item">
<div class="entity-name">
<span>${cred.name || 'Unnamed' }</span>
</div>
<div class="buttons">
<mwc-icon-button data-cred-id="${cred.id}"
data-name="${cred.name || 'Unnamed' }" @click="${rename}"
icon="edit"></mwc-icon-button>
<mwc-icon-button data-cred-id="${cred.id}" @click="${remove}"
icon="delete"></mwc-icon-button>
</div>
</div>
</mwc-list-item>`)}
</mwc-list>` : html`
<mwc-list>
<mwc-list-item>No credentials found.</mwc-list-item>
</mwc-list>`}`;
render(creds, list);
};
- Rufen Sie in der nächsten Zeile die Funktion
renderCredentials()
auf, um registrierte Passkeys anzuzeigen, sobald der Nutzer auf der Seite/home
landet.
views/home.html
renderCredentials();
Passkey erstellen und registrieren
Wenn Sie einen Passkey erstellen und registrieren möchten, müssen Sie die Funktion registerCredential()
aufrufen, die Sie zuvor implementiert haben.
So lösen Sie die registerCredential()
-Funktion aus, wenn Sie auf die Schaltfläche Passkey erstellen klicken:
- Suchen Sie in der Datei nach dem Platzhalter-HTML nach der folgenden
import
-Anweisung:
views/home.html
import {
$,
_fetch,
loading,
updateCredential,
unregisterCredential,
} from '/client.js';
- Fügen Sie am Ende des Texts der
import
-Anweisung die FunktionregisterCredential()
ein.
views/home.html
// TODO: Add an ability to create a passkey: Create and register a passkey.
import {
$,
_fetch,
loading,
updateCredential,
unregisterCredential,
registerCredential
} from '/client.js';
- Definieren Sie am Ende der Datei nach dem relevanten Kommentar eine
register()
-Funktion, die dieregisterCredential()
-Funktion und eine Lade-UI aufruft undrenderCredentials()
nach einer Registrierung aufruft. Es wird klargestellt, dass der Browser einen Passkey erstellt und eine Fehlermeldung anzeigt, wenn etwas schiefgeht.
views/home.html
// TODO: Add an ability to create a passkey: Create and register a passkey.
async function register() {
try {
// Start the loading UI.
loading.start();
// Start creating a passkey.
await registerCredential();
// Stop the loading UI.
loading.stop();
// Render the updated passkey list.
renderCredentials();
- Fangen Sie Ausnahmen im Hauptteil der
register()
-Funktion ab. Die Methodenavigator.credentials.create()
gibt einenInvalidStateError
-Fehler aus, wenn auf dem Gerät bereits ein Passkey vorhanden ist. Dies wird mit demexcludeCredentials
-Array untersucht. In diesem Fall wird dem Nutzer eine relevante Nachricht angezeigt. Außerdem wird einNotAllowedError
-Fehler ausgegeben, wenn der Nutzer das Authentifizierungsdialogfeld schließt. In diesem Fall ignorieren Sie sie einfach.
views/home.html
} catch (e) {
// Stop the loading UI.
loading.stop();
// An InvalidStateError indicates that a passkey already exists on the device.
if (e.name === 'InvalidStateError') {
alert('A passkey already exists for this device.');
// A NotAllowedError indicates that the user canceled the operation.
} else if (e.name === 'NotAllowedError') {
Return;
// Show other errors in an alert.
} else {
alert(e.message);
console.error(e);
}
}
};
- Hängen Sie in der Zeile nach der
register()
-Funktion dieregister()
-Funktion an einclick
-Ereignis für die Schaltfläche Passkey erstellen an.
views/home.html
createPasskey.addEventListener('click', register);
Lösungscode für diesen Abschnitt ansehen
views/home.html
<!-- TODO: Add an ability to create a passkey: Add placeholder HTML. -->
<section>
<h3 class="mdc-typography mdc-typography--headline6"> Your registered
passkeys:</h3>
<div id="list"></div>
</section>
<p id="message" class="instructions"></p>
<mwc-button id="create-passkey" class="hidden" icon="fingerprint" raised>Create a passkey</mwc-button>
views/home.html
// TODO: Add an ability to create a passkey: Create and register a passkey.
import {
$,
_fetch,
loading,
updateCredential,
unregisterCredential,
registerCredential
} from '/client.js';
views/home.html
// TODO: Add an ability to create a passkey: Check for passkey support.
const createPasskey = $('#create-passkey');
// Feature detections
if (window.PublicKeyCredential &&
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&
PublicKeyCredential.isConditionalMediationAvailable) {
try {
const results = await Promise.all([
// Is platform authenticator available in this browser?
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),
// Is conditional UI available in this browser?
PublicKeyCredential.isConditionalMediationAvailable()
]);
if (results.every(r => r === true)) {
// If conditional UI is available, reveal the Create a passkey button.
createPasskey.classList.remove('hidden');
} else {
// If conditional UI isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
} catch (e) {
console.error(e);
}
} else {
// If WebAuthn isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
// TODO: Add an ability to create a passkey: Render registered passkeys in a list.
async function renderCredentials() {
const res = await _fetch('/auth/getKeys');
const list = $('#list');
const creds = html`${res.length > 0 ? html`
<mwc-list>
${res.map(cred => html`
<mwc-list-item>
<div class="list-item">
<div class="entity-name">
<span>${cred.name || 'Unnamed' }</span>
</div>
<div class="buttons">
<mwc-icon-button data-cred-id="${cred.id}" data-name="${cred.name || 'Unnamed' }" @click="${rename}" icon="edit"></mwc-icon-button>
<mwc-icon-button data-cred-id="${cred.id}" @click="${remove}" icon="delete"></mwc-icon-button>
</div>
</div>
</mwc-list-item>`)}
</mwc-list>` : html`
<mwc-list>
<mwc-list-item>No credentials found.</mwc-list-item>
</mwc-list>`}`;
render(creds, list);
};
renderCredentials();
// TODO: Add an ability to create a passkey: Create and register a passkey.
async function register() {
try {
// Start the loading UI.
loading.start();
// Start creating a passkey.
await registerCredential();
// Stop the loading UI.
loading.stop();
// Render the updated passkey list.
renderCredentials();
} catch (e) {
// Stop the loading UI.
loading.stop();
// An InvalidStateError indicates that a passkey already exists on the device.
if (e.name === 'InvalidStateError') {
alert('A passkey already exists for this device.');
// A NotAllowedError indicates that the user canceled the operation.
} else if (e.name === 'NotAllowedError') {
Return;
// Show other errors in an alert.
} else {
alert(e.message);
console.error(e);
}
}
};
createPasskey.addEventListener('click', register);
Jetzt ausprobieren
Wenn Sie alle bisherigen Schritte ausgeführt haben, haben Sie die Möglichkeit implementiert, Passkeys auf der Website zu erstellen, zu registrieren und anzuzeigen.
So probieren Sie es aus:
- Melden Sie sich auf dem Vorschau-Tab mit einem zufälligen Nutzernamen und Passwort an.
- Klicken Sie auf Passkey erstellen.
- Bestätigen Sie Ihre Identität mit der Displaysperre des Geräts.
- Prüfen Sie, ob ein Passkey registriert und im Abschnitt Ihre registrierten Passkeys der Webseite angezeigt wird.
Registrierte Passkeys umbenennen und entfernen
Sie sollten die registrierten Passkeys in der Liste umbenennen oder löschen können. Sie können sich ansehen, wie es im Code funktioniert, da es im Codelab enthalten ist.
In Chrome können Sie registrierte Passkeys auf dem Computer unter chrome://settings/passkeys oder auf Android-Geräten im Passwortmanager in den Einstellungen entfernen.
Informationen zum Umbenennen und Entfernen registrierter Passkeys auf anderen Plattformen finden Sie auf den entsprechenden Supportseiten für diese Plattformen.
5. Authentifizierung mit einem Passkey hinzufügen
Nutzer können jetzt einen Passkey erstellen und registrieren und ihn als sichere Authentifizierungsmethode für Ihre Website verwenden. Jetzt müssen Sie Ihrer Website eine Passkey-Authentifizierungsfunktion hinzufügen.
Funktion authenticate()
erstellen
- Erstellen Sie in der Datei
public/client.js
nach dem entsprechenden Kommentar eine Funktion namensauthenticate()
, die den Nutzer lokal und dann auf dem Server überprüft:
public/client.js
// TODO: Add an ability to authenticate with a passkey: Create the authenticate() function.
export async function authenticate() {
// TODO: Add an ability to authenticate with a passkey: Obtain the challenge and other options from the server endpoint.
// TODO: Add an ability to authenticate with a passkey: Locally verify the user and get a credential.
// TODO: Add an ability to authenticate with a passkey: Verify the credential.
};
Herausforderung und andere Optionen vom Serverendpunkt abrufen
Bevor Sie den Nutzer zur Authentifizierung auffordern, müssen Sie Parameter anfordern, die in WebAuthn vom Server übergeben werden sollen, einschließlich einer Challenge.
- Rufen Sie im Text der Funktion
authenticate()
nach dem entsprechenden Kommentar die Funktion_fetch()
auf, um einePOST
-Anfrage an den Server zu senden:
public/client.js
// TODO: Add an ability to authenticate with a passkey: Obtain the challenge and other options from the server endpoint.
const options = await _fetch('/auth/signinRequest');
Der Server dieses Codelabs ist so konzipiert, dass er JSON zurückgibt, das dem PublicKeyCredentialRequestOptions
-Wörterbuch, das an die WebAuthn-navigator.credentials.get()
-API übergeben wird, so ähnlich wie möglich ist. Das folgende Code-Snippet enthält Beispieloptionen, die Sie erhalten sollten:
{
"challenge": *****,
"rpId": "passkeys-codelab.glitch.me",
"allowCredentials": []
}
Die folgende Tabelle ist nicht vollständig, enthält aber die wichtigen Parameter im PublicKeyCredentialRequestOptions
-Dictionary:
Parameter | Textzeilen |
Eine vom Server generierte Challenge in einem | |
Eine RP‑ID ist eine Domain. Eine Website kann entweder ihre Domain oder ein registrierbares Suffix angeben. Dieser Wert muss mit dem Parameter | |
Mit dieser Property werden Authentifikatoren gefunden, die für diese Authentifizierung infrage kommen. Übergeben Sie ein leeres Array oder lassen Sie es nicht angegeben, damit der Browser eine Kontoauswahl anzeigt. | |
Legen Sie den Wert auf |
Nutzer lokal bestätigen und Anmeldedaten abrufen
- Konvertieren Sie im Funktionskörper von
authenticate()
nach dem relevanten Kommentar den Parameterchallenge
zurück in Binär:
public/client.js
// TODO: Add an ability to authenticate with a passkey: Locally verify the user and get a credential.
// Base64URL decode the challenge.
options.challenge = base64url.decode(options.challenge);
- Übergeben Sie ein leeres Array an den Parameter
allowCredentials
, um bei der Authentifizierung eines Nutzers eine Kontoauswahl zu öffnen:
public/client.js
// An empty allowCredentials array invokes an account selector by discoverable credentials.
options.allowCredentials = [];
Die Kontoauswahl verwendet die mit dem Passkey gespeicherten Informationen des Nutzers.
- Rufen Sie die Methode
navigator.credentials.get()
zusammen mit der Optionmediation: 'conditional'
auf:
public/client.js
// Invoke the WebAuthn get() method.
const cred = await navigator.credentials.get({
publicKey: options,
// Request a conditional UI.
mediation: 'conditional'
});
Mit dieser Option wird der Browser angewiesen, Passkeys bedingt als Teil der automatischen Formularausfüllung vorzuschlagen.
Anmeldedaten überprüfen
Nachdem der Nutzer seine Identität lokal bestätigt hat, sollten Sie ein Anmeldedatenobjekt mit einer Signatur erhalten, die Sie auf dem Server überprüfen können.
Das folgende Code-Snippet enthält ein Beispiel für ein PublicKeyCredential
-Objekt:
{
"id": *****,
"rawId": *****,
"type": "public-key",
"response": {
"clientDataJSON": *****,
"authenticatorData": *****,
"signature": *****,
"userHandle": *****
},
authenticatorAttachment: "platform"
}
Die folgende Tabelle ist nicht vollständig, enthält aber die wichtigen Parameter im PublicKeyCredential
-Objekt:
Parameter | Textzeilen |
Die Base64URL-codierte ID des authentifizierten Passkey-Anmeldedaten. | |
Eine | |
Ein | |
Ein | |
Ein | |
Ein | |
Gibt einen |
So senden Sie das Anmeldedatenobjekt an den Server:
- Codieren Sie im Hauptteil der Funktion
authenticate()
nach dem entsprechenden Kommentar die binären Parameter der Anmeldedaten, damit sie als String an den Server gesendet werden können:
public/client.js
// TODO: Add an ability to authenticate with a passkey: Verify the credential.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// Base64URL encode some values.
const clientDataJSON = base64url.encode(cred.response.clientDataJSON);
const authenticatorData = base64url.encode(cred.response.authenticatorData);
const signature = base64url.encode(cred.response.signature);
const userHandle = base64url.encode(cred.response.userHandle);
credential.response = {
clientDataJSON,
authenticatorData,
signature,
userHandle,
};
- Senden Sie das Objekt an den Server:
public/client.js
return await _fetch(`/auth/signinResponse`, credential);
Wenn Sie das Programm ausführen, gibt der Server HTTP code 200
zurück. Das bedeutet, dass die Anmeldedaten bestätigt wurden.
Sie haben jetzt die vollständige Funktion authentication()
.
Lösungscode für diesen Abschnitt ansehen
public/client.js
// TODO: Add an ability to authenticate with a passkey: Create the authenticate() function.
export async function authenticate() {
// TODO: Add an ability to authenticate with a passkey: Obtain the
challenge and other options from the server endpoint.
const options = await _fetch('/auth/signinRequest');
// TODO: Add an ability to authenticate with a passkey: Locally verify
the user and get a credential.
// Base64URL decode the challenge.
options.challenge = base64url.decode(options.challenge);
// The empty allowCredentials array invokes an account selector
by discoverable credentials.
options.allowCredentials = [];
// Invoke the WebAuthn get() function.
const cred = await navigator.credentials.get({
publicKey: options,
// Request a conditional UI.
mediation: 'conditional'
});
// TODO: Add an ability to authenticate with a passkey: Verify the credential.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// Base64URL encode some values.
const clientDataJSON = base64url.encode(cred.response.clientDataJSON);
const authenticatorData =
base64url.encode(cred.response.authenticatorData);
const signature = base64url.encode(cred.response.signature);
const userHandle = base64url.encode(cred.response.userHandle);
credential.response = {
clientDataJSON,
authenticatorData,
signature,
userHandle,
};
return await _fetch(`/auth/signinResponse`, credential);
};
6. Passkeys zur Browser-Autofill-Funktion hinzufügen
Wenn der Nutzer zurückkehrt, soll er sich so einfach und sicher wie möglich anmelden können. Wenn Sie der Anmeldeseite die Schaltfläche Mit Passkey anmelden hinzufügen, kann der Nutzer auf die Schaltfläche tippen, im Kontoauswahlfeld des Browsers einen Passkey auswählen und die Identität über die Displaysperre bestätigen.
Die Umstellung von Passwörtern auf Passkeys erfolgt jedoch nicht für alle Nutzer gleichzeitig. Das bedeutet, dass Sie Passwörter erst entfernen können, wenn alle Nutzer auf Passkeys umgestellt haben. Bis dahin müssen Sie das Anmeldeformular mit Passwort beibehalten. Wenn Sie jedoch ein Passwortformular und eine Passkey-Schaltfläche einfügen, müssen Nutzer unnötigerweise auswählen, welche Methode sie für die Anmeldung verwenden möchten. Im Idealfall ist die Anmeldung unkompliziert.
Hier kommt eine bedingte Benutzeroberfläche ins Spiel. Eine bedingte Benutzeroberfläche ist eine WebAuthn-Funktion, mit der Sie ein Formulareingabefeld erstellen können, um zusätzlich zu Passwörtern auch einen Passkey als Teil der Autofill-Elemente vorzuschlagen. Wenn ein Nutzer in den Vorschlägen für die automatische Vervollständigung auf einen Passkey tippt, wird er aufgefordert, seine Identität lokal mit der Displaysperre des Geräts zu bestätigen. Das ist eine nahtlose Nutzererfahrung, da die Nutzeraktion fast identisch mit der einer passwortbasierten Anmeldung ist.
Bedingte Benutzeroberfläche aktivieren
Um eine bedingte Benutzeroberfläche zu aktivieren, müssen Sie lediglich ein webauthn
-Token in das Attribut autocomplete
eines Eingabefelds einfügen. Wenn das Token festgelegt ist, können Sie die Methode navigator.credentials.get()
mit dem String mediation: 'conditional'
aufrufen, um die Benutzeroberfläche für die Displaysperre bedingt auszulösen.
- Um eine bedingte Benutzeroberfläche zu aktivieren, ersetzen Sie die vorhandenen Eingabefelder für den Nutzernamen nach dem entsprechenden Kommentar in der Datei
view/index.html
durch den folgenden HTML-Code:
view/index.html
<!-- TODO: Add passkeys to the browser autofill: Enable conditional UI. -->
<input
type="text"
id="username"
class="mdc-text-field__input"
aria-labelledby="username-label"
name="username"
autocomplete="username webauthn"
autofocus />
Funktionen erkennen, WebAuthn aufrufen und eine bedingte Benutzeroberfläche aktivieren
- Ersetzen Sie in der Datei
view/index.html
nach dem entsprechenden Kommentar die vorhandeneimport
-Anweisung durch den folgenden Code:
view/index.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
import {
$,
_fetch,
loading,
authenticate
} from "/client.js";
Mit diesem Code wird die Funktion authenticate()
importiert, die Sie zuvor implementiert haben.
- Prüfen Sie, ob das
window.PulicKeyCredential
-Objekt verfügbar ist und ob diePublicKeyCredential.isConditionalMediationAvailable()
-Methode einentrue
-Wert zurückgibt. Rufen Sie dann dieauthenticate()
-Funktion auf:
view/index.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
if (
window.PublicKeyCredential &&
PublicKeyCredential.isConditionalMediationAvailable
) {
try {
// Is conditional UI available in this browser?
const cma =
await PublicKeyCredential.isConditionalMediationAvailable();
if (cma) {
// If conditional UI is available, invoke the authenticate() function.
const user = await authenticate();
if (user) {
// Proceed only when authentication succeeds.
$("#username").value = user.username;
loading.start();
location.href = "/home";
} else {
throw new Error("User not found.");
}
}
} catch (e) {
loading.stop();
// A NotAllowedError indicates that the user canceled the operation.
if (e.name !== "NotAllowedError") {
console.error(e);
alert(e.message);
}
}
}
Lösungscode für diesen Abschnitt ansehen
view/index.html
<!-- TODO: Add passkeys to the browser autofill: Enable conditional UI. -->
<input
type="text"
id="username"
class="mdc-text-field__input"
aria-labelledby="username-label"
name="username"
autocomplete="username webauthn"
autofocus
/>
view/index.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
import {
$,
_fetch,
loading,
authenticate
} from '/client.js';
view/index.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
// Is WebAuthn avaiable in this browser?
if (window.PublicKeyCredential &&
PublicKeyCredential.isConditionalMediationAvailable) {
try {
// Is a conditional UI available in this browser?
const cma= await PublicKeyCredential.isConditionalMediationAvailable();
if (cma) {
// If a conditional UI is available, invoke the authenticate() function.
const user = await authenticate();
if (user) {
// Proceed only when authentication succeeds.
$('#username').value = user.username;
loading.start();
location.href = '/home';
} else {
throw new Error('User not found.');
}
}
} catch (e) {
loading.stop();
// A NotAllowedError indicates that the user canceled the operation.
if (e.name !== 'NotAllowedError') {
console.error(e);
alert(e.message);
}
}
}
Jetzt ausprobieren
Sie haben die Erstellung, Registrierung, Anzeige und Authentifizierung von Passkeys auf Ihrer Website implementiert.
So probieren Sie es aus:
- Rufen Sie den Tab „Vorschau“ auf.
- Melden Sie sich gegebenenfalls ab.
- Klicken Sie auf das Textfeld für den Nutzernamen. Ein Dialogfeld wird angezeigt.
- Wählen Sie das Konto aus, mit dem Sie sich anmelden möchten.
- Bestätigen Sie Ihre Identität mit der Displaysperre des Geräts. Sie werden zur Seite
/home
weitergeleitet und sind angemeldet.
7. Glückwunsch!
Sie haben dieses Codelab abgeschlossen. Wenn Sie Fragen haben, stellen Sie sie auf der FIDO-DEV-Mailingliste oder auf Stack Overflow mit dem Tag passkey
.