feat: Passwordless cross-device authentication

- Arkitektur: docs/auth/passwordless-architecture.md
- Backend: iom/quixzoom-auth-service/ (FastAPI + Redis)
- Webb: quixzoom-market-pages/se/login/ (QR-kod + polling)
- App: iom/quixzoom-app/src/features/auth/ (push + deep links)

Flöde: QR-kod → app-godkännande → webb-inloggad
This commit is contained in:
Bernt
2026-07-07 07:11:50 +00:00
parent 4aa984ad74
commit 6989a98d75
61843 changed files with 5491611 additions and 872231 deletions
@@ -120,7 +120,7 @@
background: #e94560;
color: white;
padding: 1rem 2rem;
border-radius: 8px;
border-radius: var(--radius-md);
text-decoration: none;
font-weight: 600;
margin: 0.5rem;
@@ -172,7 +172,7 @@
.feature-card {
background: white;
padding: 2rem;
border-radius: 12px;
border-radius: var(--radius-md);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
@@ -184,7 +184,7 @@
margin-bottom: 1.5rem;
padding: 1.5rem;
background: #f8f9fa;
border-radius: 8px;
border-radius: var(--radius-md);
}
.cta-section {
@@ -350,75 +350,75 @@
</tr>
<tr>
<td><strong>AI-Powered Analysis</strong></td>
<td class="winner"> Full orchestration</td>
<td> Basic tagging</td>
<td> None</td>
<td> Templates only</td>
<td> None</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full orchestration</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Basic tagging</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Templates only</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr class="highlight">
<td><strong>Video Support</strong></td>
<td class="winner"> 4K/8K + metadata</td>
<td> Photos only</td>
<td> Basic video</td>
<td> Limited</td>
<td> None</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 4K/8K + metadata</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Photos only</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Basic video</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Limited</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr>
<td><strong>Predictive Analytics</strong></td>
<td class="winner"> 0-100 scoring</td>
<td> None</td>
<td> None</td>
<td> None</td>
<td> None</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 0-100 scoring</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr class="highlight">
<td><strong>Real-time Updates</strong></td>
<td class="winner"> Live intelligence</td>
<td> Batch upload</td>
<td> Near real-time</td>
<td> Near real-time</td>
<td> Delayed</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Live intelligence</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Batch upload</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Near real-time</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Near real-time</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Delayed</td>
</tr>
<tr>
<td><strong>Drone Integration</strong></td>
<td class="winner"> Native support</td>
<td> Manual upload</td>
<td> Third-party</td>
<td> None</td>
<td> N/A</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Native support</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Manual upload</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Third-party</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> N/A</td>
</tr>
<tr class="highlight">
<td><strong>Knowledge Graph</strong></td>
<td class="winner"> Built-in</td>
<td> None</td>
<td> None</td>
<td> None</td>
<td> Spreadsheets</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Built-in</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Spreadsheets</td>
</tr>
<tr>
<td><strong>Active Learning</strong></td>
<td class="winner"> RALE engine</td>
<td> Static</td>
<td> Static</td>
<td> Static</td>
<td> N/A</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> RALE engine</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Static</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Static</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Static</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> N/A</td>
</tr>
<tr class="highlight">
<td><strong>Contradiction Detection</strong></td>
<td class="winner"> AI-powered</td>
<td> None</td>
<td> None</td>
<td> None</td>
<td> Manual</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> AI-powered</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Manual</td>
</tr>
<tr>
<td><strong>Deployment Time</strong></td>
<td class="winner"> 24-72 hours</td>
<td> 2-4 weeks</td>
<td> 1-2 weeks</td>
<td> 1-2 weeks</td>
<td> Months</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 24-72 hours</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 2-4 weeks</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 1-2 weeks</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> 1-2 weeks</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Months</td>
</tr>
<tr class="highlight">
<td><strong>Pricing Model</strong></td>
@@ -430,35 +430,35 @@
</tr>
<tr>
<td><strong>Mobile App</strong></td>
<td class="winner"> quiXzoom (iOS/Android)</td>
<td> iOS/Android</td>
<td> iOS/Android</td>
<td> iOS/Android</td>
<td> Paper forms</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> quiXzoom (iOS/Android)</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> iOS/Android</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> iOS/Android</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> iOS/Android</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Paper forms</td>
</tr>
<tr class="highlight">
<td><strong>Offline Capability</strong></td>
<td class="winner"> Full offline sync</td>
<td> Limited</td>
<td> Yes</td>
<td> Yes</td>
<td> Always offline</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full offline sync</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Limited</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Yes</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Yes</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Always offline</td>
</tr>
<tr>
<td><strong>API Access</strong></td>
<td class="winner"> Full REST API</td>
<td> Limited</td>
<td> Yes</td>
<td> Limited</td>
<td> None</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Full REST API</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Limited</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Yes</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Limited</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> None</td>
</tr>
<tr class="highlight">
<td><strong>Custom Reports</strong></td>
<td class="winner"> Automated + custom</td>
<td> Templates</td>
<td> Customizable</td>
<td> Templates</td>
<td> Manual</td>
<td class="winner"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Automated + custom</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Templates</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Customizable</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Templates</td>
<td><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Manual</td>
</tr>
</tbody>
</table>
@@ -470,27 +470,27 @@
<h2 class="section-title">Key Differentiators</h2>
<div class="features-grid">
<div class="feature-card">
<h3>🎯 Purpose-Built for Infrastructure</h3>
<h3> Purpose-Built for Infrastructure</h3>
<p>Unlike general-purpose tools, LandveX RIOS is designed specifically for infrastructure monitoring. Every feature — from video capture to predictive scoring — is optimized for bridges, roads, utilities, and urban assets.</p>
</div>
<div class="feature-card">
<h3>🧠 AI That Learns</h3>
<h3> AI That Learns</h3>
<p>RALE (Reinforcement Active Learning Engine) means your system gets smarter with every observation. Other platforms use static models that never improve without manual updates.</p>
</div>
<div class="feature-card">
<h3>📹 Video-First Approach</h3>
<h3> Video-First Approach</h3>
<p>While competitors focus on photos, LandveX captures continuous 4K/8K video with full metadata. This provides context that static images cannot match — critical for infrastructure assessment.</p>
</div>
<div class="feature-card">
<h3> Speed to Intelligence</h3>
<h3><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M9 2L5 9H8L7 14L11 7H8L9 2Z" fill="#f59e0b"/></svg> Speed to Intelligence</h3>
<p>24-72 hours from mission creation to structured intelligence. Traditional methods take weeks or months. Other software platforms require 1-4 weeks of setup and training.</p>
</div>
<div class="feature-card">
<h3>🌍 Global Scale</h3>
<h3> Global Scale</h3>
<p>quiXzoom network provides verified field contributors in 100+ cities without requiring local staffing. Deploy in Stockholm, Singapore, or São Paulo with equal rigour.</p>
</div>
<div class="feature-card">
<h3>💰 Usage-Based Pricing</h3>
<h3> Usage-Based Pricing</h3>
<p>Pay for intelligence, not seats. Unlike per-user pricing that penalizes growth, LandveX scales with your actual observation volume — fair and predictable.</p>
</div>
</div>