Refactor Passkey Registration to utilize native WebAuthn API
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 44s
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 44s
Update the test-smartphone.html to replace the use of @simplewebauthn/browser with the native WebAuthn API for Passkey registration. Enhance user feedback with additional messages regarding the use of the native API and improve the handling of user ID formats. Include detailed logging of credential creation options and ensure proper serialization of credential data for better debugging and compliance.
This commit is contained in:
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user