🔐 Implémentation PBKDF2 avec credentials navigateur ✅ Fonctionnalités ajoutées: - SecureCredentialsService avec PBKDF2 (100k itérations) - Chiffrement AES-GCM des clés spend/scan - Interface utilisateur complète pour gestion credentials - Tests unitaires complets - Architecture modulaire avec EventBus - Gestion mémoire optimisée - Performance monitoring - Web Workers pour encodage asynchrone 🛡️ Sécurité: - Dérivation PBKDF2 avec salt unique - Chiffrement AES-GCM des clés sensibles - Validation force mot de passe - Stockage sécurisé IndexedDB + WebAuthn - Logging sécurisé sans exposition données 🔧 Corrections: - Erreur 500 résolue (clé dupliquée package.json) - Configuration Vite simplifiée - Dépendances manquantes corrigées 📊 Améliorations: - Architecture découplée avec repositories - Services spécialisés (PairingService, etc.) - Monitoring performance et mémoire - Tests avec couverture complète - Documentation technique détaillée
328 lines
9.7 KiB
HTML
328 lines
9.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>External Site - 4NK Integration Example</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.header {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
padding: 20px;
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.header h1 {
|
|
color: #333;
|
|
text-align: center;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.header p {
|
|
color: #666;
|
|
text-align: center;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.main-content {
|
|
padding: 20px;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.integration-section {
|
|
background: rgba(255, 255, 255, 0.9);
|
|
border-radius: 12px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.integration-section h2 {
|
|
color: #333;
|
|
margin-bottom: 15px;
|
|
font-size: 24px;
|
|
}
|
|
|
|
.integration-section p {
|
|
color: #666;
|
|
margin-bottom: 20px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.iframe-container {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 600px;
|
|
border: 2px solid #e0e0e0;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.iframe-container iframe {
|
|
width: 100%;
|
|
height: 100%;
|
|
border: none;
|
|
}
|
|
|
|
.status-panel {
|
|
background: #f8f9fa;
|
|
border: 1px solid #e9ecef;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.status-panel h3 {
|
|
color: #333;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.status-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 5px 0;
|
|
border-bottom: 1px solid #e9ecef;
|
|
}
|
|
|
|
.status-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.status-label {
|
|
font-weight: 500;
|
|
color: #555;
|
|
}
|
|
|
|
.status-value {
|
|
color: #007bff;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.controls {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-top: 20px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.btn {
|
|
background: #007bff;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.btn:hover {
|
|
background: #0056b3;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.btn.secondary {
|
|
background: #6c757d;
|
|
}
|
|
|
|
.btn.secondary:hover {
|
|
background: #545b62;
|
|
}
|
|
|
|
.log-container {
|
|
background: #1e1e1e;
|
|
color: #f8f9fa;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 12px;
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.log-entry {
|
|
margin-bottom: 5px;
|
|
padding: 2px 0;
|
|
}
|
|
|
|
.log-entry.info {
|
|
color: #17a2b8;
|
|
}
|
|
|
|
.log-entry.success {
|
|
color: #28a745;
|
|
}
|
|
|
|
.log-entry.error {
|
|
color: #dc3545;
|
|
}
|
|
|
|
.log-entry.warning {
|
|
color: #ffc107;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>🏢 External Business Site</h1>
|
|
<p>Integrated 4NK Pairing System - Secure Device Authentication</p>
|
|
</div>
|
|
|
|
<div class="main-content">
|
|
<div class="integration-section">
|
|
<h2>🔐 4NK Pairing Integration</h2>
|
|
<p>
|
|
This external site demonstrates how to integrate the 4NK pairing system
|
|
using an iframe with channel_message communication. The iframe contains
|
|
the 4NK application without header, and all menu options are integrated
|
|
as buttons within the content.
|
|
</p>
|
|
|
|
<div class="iframe-container">
|
|
<iframe
|
|
id="4nk-iframe"
|
|
src="http://localhost:3004"
|
|
title="4NK Pairing System"
|
|
sandbox="allow-scripts allow-same-origin allow-forms"
|
|
></iframe>
|
|
</div>
|
|
|
|
<div class="status-panel">
|
|
<h3>📊 Integration Status</h3>
|
|
<div class="status-item">
|
|
<span class="status-label">Iframe Status:</span>
|
|
<span class="status-value" id="iframe-status">Loading...</span>
|
|
</div>
|
|
<div class="status-item">
|
|
<span class="status-label">Communication:</span>
|
|
<span class="status-value" id="communication-status">Waiting...</span>
|
|
</div>
|
|
<div class="status-item">
|
|
<span class="status-label">Last Message:</span>
|
|
<span class="status-value" id="last-message">None</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="controls">
|
|
<button class="btn" onclick="sendTestMessage()">📤 Send Test Message</button>
|
|
<button class="btn secondary" onclick="clearLog()">🗑️ Clear Log</button>
|
|
<button class="btn secondary" onclick="refreshIframe()">🔄 Refresh Iframe</button>
|
|
</div>
|
|
|
|
<div class="log-container" id="log-container">
|
|
<div class="log-entry info">🚀 External site loaded</div>
|
|
<div class="log-entry info">📡 Waiting for iframe communication...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let messageCount = 0;
|
|
|
|
// Listen for messages from the iframe
|
|
window.addEventListener('message', function(event) {
|
|
// Security check - in production, verify event.origin
|
|
if (event.origin !== 'http://localhost:3004') {
|
|
return;
|
|
}
|
|
|
|
const { type, data } = event.data;
|
|
messageCount++;
|
|
|
|
logMessage(`📨 Received: ${type}`, 'info');
|
|
updateStatus('communication-status', 'Active');
|
|
updateStatus('last-message', `${type} (${messageCount})`);
|
|
|
|
// Handle different message types
|
|
switch (type) {
|
|
case 'IFRAME_READY':
|
|
logMessage('✅ 4NK iframe is ready', 'success');
|
|
updateStatus('iframe-status', 'Ready');
|
|
break;
|
|
|
|
case 'MENU_NAVIGATION':
|
|
logMessage(`🧭 Menu navigation: ${data.page}`, 'info');
|
|
break;
|
|
|
|
case 'PAIRING_4WORDS_WORDS_GENERATED':
|
|
logMessage(`🔐 4 words generated: ${data.words}`, 'success');
|
|
break;
|
|
|
|
case 'PAIRING_4WORDS_STATUS_UPDATE':
|
|
logMessage(`📊 Status update: ${data.status}`, 'info');
|
|
break;
|
|
|
|
case 'PAIRING_4WORDS_SUCCESS':
|
|
logMessage(`✅ Pairing successful: ${data.message}`, 'success');
|
|
break;
|
|
|
|
case 'PAIRING_4WORDS_ERROR':
|
|
logMessage(`❌ Pairing error: ${data.error}`, 'error');
|
|
break;
|
|
|
|
default:
|
|
logMessage(`❓ Unknown message type: ${type}`, 'warning');
|
|
}
|
|
});
|
|
|
|
function logMessage(message, type = 'info') {
|
|
const logContainer = document.getElementById('log-container');
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
const logEntry = document.createElement('div');
|
|
logEntry.className = `log-entry ${type}`;
|
|
logEntry.textContent = `[${timestamp}] ${message}`;
|
|
logContainer.appendChild(logEntry);
|
|
logContainer.scrollTop = logContainer.scrollHeight;
|
|
}
|
|
|
|
function updateStatus(elementId, value) {
|
|
const element = document.getElementById(elementId);
|
|
if (element) {
|
|
element.textContent = value;
|
|
}
|
|
}
|
|
|
|
function sendTestMessage() {
|
|
const iframe = document.getElementById('4nk-iframe');
|
|
if (iframe && iframe.contentWindow) {
|
|
iframe.contentWindow.postMessage({
|
|
type: 'TEST_MESSAGE',
|
|
data: { message: 'Hello from external site!' }
|
|
}, 'http://localhost:3004');
|
|
logMessage('📤 Sent test message to iframe', 'info');
|
|
}
|
|
}
|
|
|
|
function clearLog() {
|
|
const logContainer = document.getElementById('log-container');
|
|
logContainer.innerHTML = '<div class="log-entry info">🗑️ Log cleared</div>';
|
|
}
|
|
|
|
function refreshIframe() {
|
|
const iframe = document.getElementById('4nk-iframe');
|
|
iframe.src = iframe.src;
|
|
logMessage('🔄 Iframe refreshed', 'info');
|
|
updateStatus('iframe-status', 'Refreshing...');
|
|
}
|
|
|
|
// Initialize
|
|
logMessage('🌐 External site initialized', 'success');
|
|
</script>
|
|
</body>
|
|
</html>
|