Add CORS testing documentation and HTML test page for Passkey Cross-Device Authentication
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 55s
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 55s
Introduce a comprehensive CORS testing guide in CORS_TEST_ANLEITUNG.md, detailing steps for testing OPTIONS and POST requests, along with expected responses. Additionally, add a new HTML test page (test-cors.html) to facilitate interactive testing of CORS headers and responses for the Passkey registration API. Update the server API to ensure proper CORS headers are set for Cross-Device Authentication, enhancing the overall testing and debugging process.
This commit is contained in:
214
public/test-cors.html
Normal file
214
public/test-cors.html
Normal file
@@ -0,0 +1,214 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CORS Test für Passkey Cross-Device</title>
|
||||
<style>
|
||||
body { font-family: monospace; padding: 20px; }
|
||||
.test { margin: 20px 0; padding: 10px; border: 1px solid #ccc; }
|
||||
.success { background: #d4edda; }
|
||||
.error { background: #f8d7da; }
|
||||
.info { background: #d1ecf1; }
|
||||
button { padding: 10px 20px; margin: 5px; }
|
||||
pre { background: #f5f5f5; padding: 10px; overflow-x: auto; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>CORS Test für Passkey Cross-Device Authentication</h1>
|
||||
|
||||
<div class="test info">
|
||||
<h3>1. Origin-Info</h3>
|
||||
<p><strong>Current Origin:</strong> <span id="currentOrigin"></span></p>
|
||||
<p><strong>Is Secure Context:</strong> <span id="isSecure"></span></p>
|
||||
</div>
|
||||
|
||||
<div class="test">
|
||||
<h3>2. OPTIONS Preflight Test</h3>
|
||||
<button onclick="testOptions()">Test OPTIONS Request</button>
|
||||
<pre id="optionsResult"></pre>
|
||||
</div>
|
||||
|
||||
<div class="test">
|
||||
<h3>3. POST Request Test (mit Origin-Header)</h3>
|
||||
<button onclick="testPost()">Test POST Request</button>
|
||||
<pre id="postResult"></pre>
|
||||
</div>
|
||||
|
||||
<div class="test">
|
||||
<h3>4. Registration Options Test</h3>
|
||||
<button onclick="testRegistrationOptions()">Test /api/auth/register-passkey-options</button>
|
||||
<pre id="registrationResult"></pre>
|
||||
</div>
|
||||
|
||||
<div class="test">
|
||||
<h3>5. CORS Headers Check (Network Tab)</h3>
|
||||
<p>Öffne die Browser-Entwicklertools (F12) → Network Tab</p>
|
||||
<p>Führe die Tests oben aus und prüfe:</p>
|
||||
<ul>
|
||||
<li><strong>OPTIONS Request:</strong> Sollte Status 204 haben</li>
|
||||
<li><strong>Response Headers:</strong> Sollten enthalten:
|
||||
<ul>
|
||||
<li>Access-Control-Allow-Origin: <span id="currentOrigin2"></span></li>
|
||||
<li>Access-Control-Allow-Credentials: true</li>
|
||||
<li>Access-Control-Allow-Methods: GET, POST, OPTIONS</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const origin = window.location.origin;
|
||||
document.getElementById('currentOrigin').textContent = origin;
|
||||
document.getElementById('currentOrigin2').textContent = origin;
|
||||
document.getElementById('isSecure').textContent = window.isSecureContext ? 'JA ✓' : 'NEIN ✗';
|
||||
|
||||
async function testOptions() {
|
||||
const resultEl = document.getElementById('optionsResult');
|
||||
resultEl.textContent = 'Testing OPTIONS request...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/register-passkey-options', {
|
||||
method: 'OPTIONS',
|
||||
headers: {
|
||||
'Origin': origin,
|
||||
'Access-Control-Request-Method': 'POST',
|
||||
'Access-Control-Request-Headers': 'Content-Type'
|
||||
}
|
||||
});
|
||||
|
||||
const headers = {};
|
||||
response.headers.forEach((value, key) => {
|
||||
headers[key] = value;
|
||||
});
|
||||
|
||||
resultEl.innerHTML = `
|
||||
Status: ${response.status} ${response.status === 204 ? '✓' : '✗'}
|
||||
Status Text: ${response.statusText}
|
||||
|
||||
Response Headers:
|
||||
${JSON.stringify(headers, null, 2)}
|
||||
|
||||
CORS Headers Check:
|
||||
- Access-Control-Allow-Origin: ${headers['access-control-allow-origin'] || 'FEHLT ✗'}
|
||||
- Access-Control-Allow-Credentials: ${headers['access-control-allow-credentials'] || 'FEHLT ✗'}
|
||||
- Access-Control-Allow-Methods: ${headers['access-control-allow-methods'] || 'FEHLT ✗'}
|
||||
- Access-Control-Allow-Headers: ${headers['access-control-allow-headers'] || 'FEHLT ✗'}
|
||||
`;
|
||||
|
||||
if (response.status === 204 && headers['access-control-allow-origin']) {
|
||||
resultEl.parentElement.className = 'test success';
|
||||
} else {
|
||||
resultEl.parentElement.className = 'test error';
|
||||
}
|
||||
} catch (error) {
|
||||
resultEl.textContent = `ERROR: ${error.message}\n${error.stack}`;
|
||||
resultEl.parentElement.className = 'test error';
|
||||
}
|
||||
}
|
||||
|
||||
async function testPost() {
|
||||
const resultEl = document.getElementById('postResult');
|
||||
resultEl.textContent = 'Testing POST request...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/register-passkey-options', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Origin': origin
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: 'Test User',
|
||||
email: 'test@example.com'
|
||||
})
|
||||
});
|
||||
|
||||
const headers = {};
|
||||
response.headers.forEach((value, key) => {
|
||||
headers[key] = value;
|
||||
});
|
||||
|
||||
const data = await response.json().catch(() => ({ error: 'Could not parse JSON' }));
|
||||
|
||||
resultEl.innerHTML = `
|
||||
Status: ${response.status}
|
||||
Status Text: ${response.statusText}
|
||||
|
||||
Response Headers:
|
||||
${JSON.stringify(headers, null, 2)}
|
||||
|
||||
Response Body:
|
||||
${JSON.stringify(data, null, 2)}
|
||||
|
||||
CORS Headers Check:
|
||||
- Access-Control-Allow-Origin: ${headers['access-control-allow-origin'] || 'FEHLT ✗'}
|
||||
- Access-Control-Allow-Credentials: ${headers['access-control-allow-credentials'] || 'FEHLT ✗'}
|
||||
`;
|
||||
|
||||
if (headers['access-control-allow-origin']) {
|
||||
resultEl.parentElement.className = 'test success';
|
||||
} else {
|
||||
resultEl.parentElement.className = 'test error';
|
||||
}
|
||||
} catch (error) {
|
||||
resultEl.textContent = `ERROR: ${error.message}\n${error.stack}`;
|
||||
resultEl.parentElement.className = 'test error';
|
||||
}
|
||||
}
|
||||
|
||||
async function testRegistrationOptions() {
|
||||
const resultEl = document.getElementById('registrationResult');
|
||||
resultEl.textContent = 'Testing registration options endpoint...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/register-passkey-options', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: 'Test User',
|
||||
email: 'test@example.com',
|
||||
phone: ''
|
||||
})
|
||||
});
|
||||
|
||||
const headers = {};
|
||||
response.headers.forEach((value, key) => {
|
||||
headers[key] = value;
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
resultEl.innerHTML = `
|
||||
Status: ${response.status}
|
||||
Status Text: ${response.statusText}
|
||||
|
||||
Response Headers (CORS):
|
||||
- Access-Control-Allow-Origin: ${headers['access-control-allow-origin'] || 'FEHLT ✗'}
|
||||
- Access-Control-Allow-Credentials: ${headers['access-control-allow-credentials'] || 'FEHLT ✗'}
|
||||
- Access-Control-Allow-Methods: ${headers['access-control-allow-methods'] || 'FEHLT ✗'}
|
||||
|
||||
Response Body:
|
||||
${JSON.stringify(data, null, 2)}
|
||||
|
||||
Options Structure:
|
||||
- hasChallenge: ${data.options?.challenge ? 'JA ✓' : 'NEIN ✗'}
|
||||
- hasRp: ${data.options?.rp ? 'JA ✓' : 'NEIN ✗'}
|
||||
- hasUser: ${data.options?.user ? 'JA ✓' : 'NEIN ✗'}
|
||||
- rpId: ${data.options?.rp?.id || 'FEHLT'}
|
||||
- timeout: ${data.options?.timeout || 'FEHLT'}
|
||||
`;
|
||||
|
||||
if (data.success && data.options && headers['access-control-allow-origin']) {
|
||||
resultEl.parentElement.className = 'test success';
|
||||
} else {
|
||||
resultEl.parentElement.className = 'test error';
|
||||
}
|
||||
} catch (error) {
|
||||
resultEl.textContent = `ERROR: ${error.message}\n${error.stack}`;
|
||||
resultEl.parentElement.className = 'test error';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user