diff --git a/public/test-smartphone.html b/public/test-smartphone.html
index 426bda2..46e67f2 100644
--- a/public/test-smartphone.html
+++ b/public/test-smartphone.html
@@ -328,34 +328,77 @@
throw new Error('Invalid options response');
}
- resultEl.textContent = 'Options erhalten. Starte WebAuthn startRegistration...\n\n';
+ resultEl.textContent = 'Options erhalten. Starte WebAuthn Registrierung...\n\n';
resultEl.textContent += `Challenge: ${optionsData.options.challenge?.substring(0, 20)}...\n`;
resultEl.textContent += `RP ID: ${optionsData.options.rp?.id}\n`;
resultEl.textContent += `Timeout: ${optionsData.options.timeout}ms\n\n`;
resultEl.textContent += 'Warte auf Passkey-Authentifizierung...\n';
- resultEl.textContent += '(Scanne QR-Code oder verwende lokalen Authenticator)\n';
+ resultEl.textContent += '(Scanne QR-Code oder verwende lokalen Authenticator)\n\n';
+ resultEl.textContent += 'HINWEIS: Diese Test-Seite verwendet die native WebAuthn-API.\n';
+ resultEl.textContent += 'Die eigentliche App verwendet @simplewebauthn/browser (ist im Build enthalten).\n\n';
- // WebAuthn startRegistration
- // Versuche, das Modul zu laden (kann fehlschlagen auf Smartphone)
- let startRegistration;
- try {
- const mod = await import('https://unpkg.com/@simplewebauthn/browser@13.2.2/dist/bundle/index.umd.min.js');
- startRegistration = mod.startRegistration;
- } catch (importError) {
- // Fallback: Versuche, es lokal zu laden (wenn auf der Seite verfügbar)
- resultEl.textContent += '\n\nWARNUNG: Konnte @simplewebauthn/browser nicht laden.\n';
- resultEl.textContent += 'Dies ist normal, wenn die Bibliothek nicht verfügbar ist.\n';
- resultEl.textContent += 'Die Options wurden aber erfolgreich vom Server erhalten.\n\n';
- resultEl.textContent += 'Options-Struktur:\n';
- resultEl.textContent += JSON.stringify(optionsData.options, null, 2);
- resultEl.className = 'result warn';
- return;
+ // Verwende native WebAuthn-API (navigator.credentials.create)
+ // Dies ist die Basis-API, die @simplewebauthn/browser verwendet
+ const startTime = Date.now();
+
+ // Konvertiere user.id von Base64URL zu Uint8Array (falls nötig)
+ let userId;
+ if (typeof optionsData.options.user?.id === 'string') {
+ // Base64URL decode
+ const base64Url = optionsData.options.user.id;
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
+ const binaryString = atob(base64);
+ userId = new Uint8Array(binaryString.length);
+ for (let i = 0; i < binaryString.length; i++) {
+ userId[i] = binaryString.charCodeAt(i);
+ }
+ } else if (optionsData.options.user?.id instanceof Uint8Array) {
+ userId = optionsData.options.user.id;
+ } else {
+ throw new Error('Invalid user.id format');
}
- const startTime = Date.now();
- const credential = await startRegistration(optionsData.options);
+ // Erstelle PublicKeyCredentialCreationOptions
+ const publicKeyCredentialCreationOptions = {
+ challenge: Uint8Array.from(atob(optionsData.options.challenge.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)),
+ rp: optionsData.options.rp,
+ user: {
+ id: userId,
+ name: optionsData.options.user.name,
+ displayName: optionsData.options.user.displayName
+ },
+ pubKeyCredParams: optionsData.options.pubKeyCredParams,
+ timeout: optionsData.options.timeout,
+ attestation: optionsData.options.attestation,
+ excludeCredentials: optionsData.options.excludeCredentials?.map(cred => ({
+ id: Uint8Array.from(atob(cred.id.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)),
+ type: cred.type,
+ transports: cred.transports
+ })) || [],
+ authenticatorSelection: optionsData.options.authenticatorSelection,
+ extensions: optionsData.options.extensions
+ };
+
+ console.log('[DEBUG] Calling navigator.credentials.create with options:', publicKeyCredentialCreationOptions);
+
+ const credential = await navigator.credentials.create({
+ publicKey: publicKeyCredentialCreationOptions
+ });
+
const duration = Date.now() - startTime;
+ // Konvertiere Credential zu einem serialisierbaren Format
+ const credentialData = {
+ id: credential.id,
+ rawId: Array.from(new Uint8Array(credential.rawId)),
+ type: credential.type,
+ response: {
+ clientDataJSON: Array.from(new Uint8Array(credential.response.clientDataJSON)),
+ attestationObject: Array.from(new Uint8Array(credential.response.attestationObject))
+ },
+ transports: credential.response.getTransports?.() || []
+ };
+
const result = {
'Status': 'SUCCESS ✓',
'Duration': `${duration}ms`,
@@ -364,9 +407,10 @@
'Type': credential.type,
'Raw ID': credential.rawId ? 'present' : 'missing',
'Response': credential.response ? 'present' : 'missing',
- 'Transports': credential.transports || []
+ 'Transports': credentialData.transports || []
},
- 'Full Credential': credential
+ 'Note': 'Dies ist die native WebAuthn-API. Die eigentliche App verwendet @simplewebauthn/browser.',
+ 'Credential Data': credentialData
};
resultEl.className = 'result success';