Authentifizierung mit Passkeys: Unsere Reise

Maurice Renck

Als wir ein neues Projekt begonnen haben, wollten wir die Authentifizierung einfach und sicher gestalten. Deshalb haben wir uns gegen Passwörter entschieden!

Wir hatten uns entschieden, Passkeys zu verwenden, dank ihrer einzigartigen Vorteile. In diesem Beitrag möchten wir unsere Erfahrungen und Herausforderungen bei der Implementierung der passkey-basierten Authentifizierung für unsere Webanwendung und native iOS-App teilen.

Warum Passkeys?

Wir haben uns für Passkeys anstelle herkömmlicher Passwörter entschieden, aufgrund ihrer inhärenten Sicherheitsmerkmale:

  • Sie dürfen nicht zu kurz oder einfach zu erraten sein.
  • Jedes Gerät hat einen eindeutigen Passkey.
  • Sie können nicht vergessen werden (da sie an die biometrischen Daten des Geräts gebunden sind).

Darüber hinaus verlassen wir uns auf sichere, hardwarebezogene Funktionen wie Fingerabdruckerkennung, Face ID und externe Geräte wie Yubikeys.

Herausforderungen bei der Implementierung

Unsere Reise war nicht ohne ihre Hindernisse. Wir stießen auf Probleme sowohl bei unserer Webanwendung als auch bei unserer nativen iOS-App.

Webanwendung

Für unsere Webanwendung haben wir die SimpleWebAuthn Bibliothek verwendet, um die passkey-basierte Authentifizierung zu implementieren. Die Dokumentation lieferte einen klaren Überblick über die notwendigen Schritte, einschließlich der Behandlung von Herausforderungen, um Replay-Angriffe zu verhindern.

Das Ergebnis war ein Anmeldeablauf, der eine E-Mail mit einem "Magic Link" für einmaliges Anmelden sendet. Nach erfolgreichem Login generiert unser Server ein JWT-Token, das für API-Aufrufe innerhalb der Sitzung verwendet wird.

Native iOS-App (mit React Native und react-native-passkey)

Für unsere native iOS-App haben wir react-native-passkey in unsere Expo-App integriert. Leider wurde es hier knifflig. Wir stießen auf eine Fehlermeldung, die besagte, dass keine Anmeldeinformationen zurückgegeben wurden, die scheinbar mit einem falschen Link im nativen Code zusammenhängt.

Nach Behebung dieses Problems erhielten wir eine noch allgemeinere Fehlermeldung: "Die Anfrage ist fehlgeschlagen." Nach einigen Fehlerbehebungen stellten wir fest, dass das Problem mit unserer Konfiguration der .well-known/apple-app-site-association-Datei zusammenhing. Wir konnten nicht überprüfen, ob der Simulator oder das echte Gerät diese Datei tatsächlich von unserer App anforderte und was genau den falschen Link verursachte.

Handhaben von Linking Events

Eine der größten Herausforderungen bestand darin, Linking Events innerhalb unserer React Native-App korrekt zu behandeln. Wir haben das Linking-Modul von Expo verwendet, um die URL-basierte Kommunikation zwischen unserer Webanwendung und der nativen iOS-App zu behandeln.
Um eingehende Links zu behandeln, haben wir einen Event-Listener dem Linking-Objekt mit folgendem Code hinzugefügt:

import { Linking } from 'react-native';

// ...

Linking.addEventListener('url', ({ url }) => {
  // Behandeln Sie hier die empfangene URL
});

Wir stellten jedoch schnell fest, dass dieser Ansatz nicht wie erwartet funktionierte. Der Event-Listener wurde nur ausgelöst, wenn unsere App im Vordergrund lief. Wenn sie im Hintergrundmodus lief (z. B. nach Minimieren oder in den Schlafmodus versetzt wurde), wurde das Ereignis einfach nicht ausgelöst.
Um diese Einschränkung zu überwinden, mussten wir um die Ecke denken und einen anderen Ansatz finden.

Verwendung von useLink aus Expo-Linking

Nach einigen Experimenten stellten wir fest, dass der useLink-Hook aus dem Expo-Linking-Paket eine einfachere Möglichkeit bietet, eingehende Links zu behandeln. Dieser Hook ermöglicht es, URL-Ereignisse auf robustere Weise zu erfassen.
So haben wir es verwendet:

import { useLink } from 'expo-linking';

// ...

const [link, setLink] = useState(null);

useEffect(() => {
  const listener = Linking.addEventListener('url', ({ url }) => {
    setLink(url);
  });

  return () => {
    listener.remove();
  };
}, []);

return (
  // ...
  {link && <Text>{link}</Text>}
);

Durch die Verwendung des useLink-Hooks konnten wir eingehende Links erfassen, selbst wenn unsere App im Hintergrund lief. Dies ermöglichte es uns, die URL-basierte Kommunikation effektiver zu behandeln und die allgemeine Benutzererfahrung zu verbessern.

Passkey basierter Authentifizierungsablauf

Nun, da wir einige technische Details behandelt haben, betrachten wir den passkey-basierten Authentifizierungsablauf als Ganzes.
Hier ist ein aktualisiertes Diagramm, das unseren Ansatz veranschaulicht:

Mit diesem Diagramm hoffen wir, ein klareres Verständnis des passkey-basierten Authentifizierungsprozesses zu vermitteln.

Sicher ist sicher

Wichtig ist: Sicherheit ist bei der Implementierung jeder Form von Authentifizierung zu priorisieren. Das erreichen wir, indem wir wachsam bleiben und unsere Implementierungsschwächen kontinuierlich überwachen. Damit sind wir auf dem besten Weg, eine sichere und benutzerfreundliche Erfahrung für unsere Benutzer zu schaffen.