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:
124
CORS_TEST_ANLEITUNG.md
Normal file
124
CORS_TEST_ANLEITUNG.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# CORS-Test für Passkey Cross-Device Authentication
|
||||
|
||||
## Test-Datei verwenden
|
||||
|
||||
1. **Öffne die Test-Seite:**
|
||||
```
|
||||
https://harheimertc.tsschulz.de/test-cors.html
|
||||
```
|
||||
|
||||
2. **Führe die Tests aus:**
|
||||
- Klicke auf "Test OPTIONS Request" - sollte Status 204 zurückgeben
|
||||
- Klicke auf "Test POST Request" - sollte Status 200 zurückgeben
|
||||
- Klicke auf "Test /api/auth/register-passkey-options" - sollte Options zurückgeben
|
||||
|
||||
3. **Prüfe die Browser-Entwicklertools:**
|
||||
- Öffne F12 → Network Tab
|
||||
- Führe die Tests aus
|
||||
- Klicke auf jeden Request und prüfe die **Response Headers**:
|
||||
- `Access-Control-Allow-Origin` sollte `https://harheimertc.tsschulz.de` sein
|
||||
- `Access-Control-Allow-Credentials` sollte `true` sein
|
||||
- `Access-Control-Allow-Methods` sollte `GET, POST, OPTIONS` enthalten
|
||||
|
||||
## Manueller Test mit curl
|
||||
|
||||
### OPTIONS Preflight Test:
|
||||
```bash
|
||||
curl -X OPTIONS \
|
||||
-H "Origin: https://harheimertc.tsschulz.de" \
|
||||
-H "Access-Control-Request-Method: POST" \
|
||||
-H "Access-Control-Request-Headers: Content-Type" \
|
||||
-v \
|
||||
https://harheimertc.tsschulz.de/api/auth/register-passkey-options
|
||||
```
|
||||
|
||||
**Erwartete Response:**
|
||||
- Status: 204 No Content
|
||||
- Header: `Access-Control-Allow-Origin: https://harheimertc.tsschulz.de`
|
||||
- Header: `Access-Control-Allow-Credentials: true`
|
||||
|
||||
### POST Request Test:
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Origin: https://harheimertc.tsschulz.de" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"Test","email":"test@example.com"}' \
|
||||
-v \
|
||||
https://harheimertc.tsschulz.de/api/auth/register-passkey-options
|
||||
```
|
||||
|
||||
**Erwartete Response:**
|
||||
- Status: 200 OK
|
||||
- Header: `Access-Control-Allow-Origin: https://harheimertc.tsschulz.de`
|
||||
- Body: JSON mit `success: true` und `options` Objekt
|
||||
|
||||
## Browser Console Test
|
||||
|
||||
Öffne die Browser-Konsole (F12) und führe aus:
|
||||
|
||||
```javascript
|
||||
// Test 1: OPTIONS Preflight
|
||||
fetch('https://harheimertc.tsschulz.de/api/auth/register-passkey-options', {
|
||||
method: 'OPTIONS',
|
||||
headers: {
|
||||
'Origin': window.location.origin,
|
||||
'Access-Control-Request-Method': 'POST',
|
||||
'Access-Control-Request-Headers': 'Content-Type'
|
||||
}
|
||||
}).then(r => {
|
||||
console.log('OPTIONS Status:', r.status);
|
||||
console.log('CORS Headers:', {
|
||||
origin: r.headers.get('Access-Control-Allow-Origin'),
|
||||
credentials: r.headers.get('Access-Control-Allow-Credentials'),
|
||||
methods: r.headers.get('Access-Control-Allow-Methods')
|
||||
});
|
||||
});
|
||||
|
||||
// Test 2: POST Request
|
||||
fetch('https://harheimertc.tsschulz.de/api/auth/register-passkey-options', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Origin': window.location.origin
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: 'Test User',
|
||||
email: 'test@example.com'
|
||||
})
|
||||
}).then(r => r.json()).then(data => {
|
||||
console.log('POST Response:', data);
|
||||
console.log('Has Options:', !!data.options);
|
||||
console.log('Has Challenge:', !!data.options?.challenge);
|
||||
});
|
||||
```
|
||||
|
||||
## Was zu prüfen ist:
|
||||
|
||||
1. **OPTIONS Request:**
|
||||
- Status sollte 204 sein (nicht 200 oder 404)
|
||||
- CORS-Header müssen vorhanden sein
|
||||
|
||||
2. **POST Request:**
|
||||
- Status sollte 200 sein
|
||||
- CORS-Header müssen vorhanden sein
|
||||
- Response sollte `success: true` und `options` enthalten
|
||||
|
||||
3. **Cross-Device Problem:**
|
||||
- Wenn CORS korrekt ist, aber Cross-Device trotzdem nicht funktioniert:
|
||||
- Prüfe, ob der QR-Code die richtige Origin enthält
|
||||
- Prüfe, ob das Smartphone die gleiche Origin erreichen kann
|
||||
- Prüfe die Browser-Logs auf dem Smartphone (falls möglich)
|
||||
|
||||
## Häufige Probleme:
|
||||
|
||||
1. **Apache überschreibt CORS-Header:**
|
||||
- Lösung: In Apache-Config sicherstellen, dass CORS-Header nicht überschrieben werden
|
||||
|
||||
2. **OPTIONS gibt 404 zurück:**
|
||||
- Lösung: Endpoint muss OPTIONS-Requests explizit behandeln
|
||||
|
||||
3. **CORS-Header fehlen:**
|
||||
- Lösung: Server-Endpoint muss CORS-Header setzen
|
||||
|
||||
4. **Origin-Mismatch:**
|
||||
- Lösung: `WEBAUTHN_ORIGIN` muss exakt mit der Browser-Origin übereinstimmen
|
||||
@@ -505,8 +505,7 @@ const handleRegisterWithPasskey = async () => {
|
||||
const webauthnStart = Date.now()
|
||||
|
||||
const mod = await import('@simplewebauthn/browser')
|
||||
// startRegistration erwartet die Options direkt
|
||||
// @simplewebauthn/browser v13+ erwartet die Options direkt
|
||||
// startRegistration erwartet die Options direkt (wie in anderen Dateien auch)
|
||||
let credential
|
||||
try {
|
||||
// Timeout-Warnung nach 2 Minuten
|
||||
@@ -517,29 +516,11 @@ const handleRegisterWithPasskey = async () => {
|
||||
console.warn('[DEBUG] Challenge:', pre.options?.challenge)
|
||||
}, 120000)
|
||||
|
||||
// Stelle sicher, dass die Options korrekt formatiert sind
|
||||
// @simplewebauthn/browser v13+ erwartet die Options direkt als Objekt
|
||||
const registrationOptions = {
|
||||
challenge: pre.options.challenge,
|
||||
rp: pre.options.rp,
|
||||
user: pre.options.user,
|
||||
pubKeyCredParams: pre.options.pubKeyCredParams,
|
||||
timeout: pre.options.timeout,
|
||||
attestation: pre.options.attestation || 'none',
|
||||
excludeCredentials: pre.options.excludeCredentials || [],
|
||||
authenticatorSelection: pre.options.authenticatorSelection,
|
||||
extensions: pre.options.extensions || {}
|
||||
}
|
||||
|
||||
console.log('[DEBUG] startRegistration called - QR-Code should appear now (if Cross-Device)')
|
||||
console.log('[DEBUG] Registration options structure:', {
|
||||
hasChallenge: !!registrationOptions.challenge,
|
||||
hasRp: !!registrationOptions.rp,
|
||||
hasUser: !!registrationOptions.user,
|
||||
timeout: registrationOptions.timeout
|
||||
})
|
||||
console.log('[DEBUG] Passing options directly to startRegistration (same as in profil.vue)')
|
||||
|
||||
credential = await mod.startRegistration(registrationOptions)
|
||||
// Direkt die Options übergeben (wie in profil.vue und passkey-wiederherstellen.vue)
|
||||
credential = await mod.startRegistration(pre.options)
|
||||
|
||||
clearTimeout(timeoutWarning)
|
||||
|
||||
|
||||
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>
|
||||
@@ -125,17 +125,28 @@ export default defineEventHandler(async (event) => {
|
||||
await writeAuditLog('auth.passkey.prereg.options', { email })
|
||||
|
||||
// CORS-Header für Cross-Device Authentication
|
||||
if (requestOrigin) {
|
||||
setHeader(event, 'Access-Control-Allow-Origin', requestOrigin)
|
||||
// WICHTIG: Für Cross-Device muss CORS korrekt konfiguriert sein
|
||||
const allowedOrigin = requestOrigin || webauthnOrigin
|
||||
|
||||
if (allowedOrigin) {
|
||||
setHeader(event, 'Access-Control-Allow-Origin', allowedOrigin)
|
||||
setHeader(event, 'Access-Control-Allow-Credentials', 'true')
|
||||
setHeader(event, 'Access-Control-Allow-Methods', 'POST, OPTIONS')
|
||||
setHeader(event, 'Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
||||
console.log('[DEBUG] CORS headers set', { origin: requestOrigin })
|
||||
setHeader(event, 'Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
||||
setHeader(event, 'Access-Control-Allow-Headers', 'Content-Type, Authorization, Origin, X-Requested-With')
|
||||
setHeader(event, 'Access-Control-Max-Age', '86400') // 24 Stunden Cache für Preflight
|
||||
console.log('[DEBUG] CORS headers set', {
|
||||
origin: allowedOrigin,
|
||||
requestOrigin,
|
||||
webauthnOrigin,
|
||||
method: getMethod(event)
|
||||
})
|
||||
}
|
||||
|
||||
// OPTIONS Preflight-Request für Cross-Device
|
||||
if (getMethod(event) === 'OPTIONS') {
|
||||
console.log('[DEBUG] OPTIONS request, returning early')
|
||||
return { success: true }
|
||||
console.log('[DEBUG] OPTIONS preflight request, returning 204')
|
||||
setResponseStatus(event, 204)
|
||||
return null
|
||||
}
|
||||
|
||||
// Stelle sicher, dass die Options korrekt serialisiert werden
|
||||
|
||||
@@ -11,12 +11,30 @@ import { assertPasswordNotPwned } from '../../utils/hibp.js'
|
||||
export default defineEventHandler(async (event) => {
|
||||
const requestStart = Date.now()
|
||||
const requestOrigin = getHeader(event, 'origin')
|
||||
const { origin: webauthnOrigin } = getWebAuthnConfig()
|
||||
|
||||
console.log('[DEBUG] register-passkey request received', {
|
||||
origin: requestOrigin,
|
||||
webauthnOrigin,
|
||||
timestamp: new Date().toISOString()
|
||||
})
|
||||
|
||||
// CORS-Header für Cross-Device Authentication
|
||||
const allowedOrigin = requestOrigin || webauthnOrigin
|
||||
if (allowedOrigin) {
|
||||
setHeader(event, 'Access-Control-Allow-Origin', allowedOrigin)
|
||||
setHeader(event, 'Access-Control-Allow-Credentials', 'true')
|
||||
setHeader(event, 'Access-Control-Allow-Methods', 'POST, OPTIONS')
|
||||
setHeader(event, 'Access-Control-Allow-Headers', 'Content-Type, Authorization, Origin, X-Requested-With')
|
||||
}
|
||||
|
||||
// OPTIONS Preflight-Request
|
||||
if (getMethod(event) === 'OPTIONS') {
|
||||
console.log('[DEBUG] OPTIONS preflight request, returning 204')
|
||||
setResponseStatus(event, 204)
|
||||
return null
|
||||
}
|
||||
|
||||
const body = await readBody(event)
|
||||
const registrationId = String(body?.registrationId || '')
|
||||
const response = body?.credential
|
||||
|
||||
214
test-cors.html
Normal file
214
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