feat(auth): v2.1.0 - MFA TOTP avec QR code, recovery codes
CI / lint (push) Failing after 16s
CI / test (push) Skipped
CI / build (push) Skipped
CI / e2e (push) Skipped
CI / security (push) Successful in 21s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
CI / lint (push) Failing after 16s
CI / test (push) Skipped
CI / build (push) Skipped
CI / e2e (push) Skipped
CI / security (push) Successful in 21s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
- backend/auth/mfa.py: TOTP (pyotp), recovery codes SHA-256 - Login flow: mfa_required → totp/verify → token (ou recovery) - 6 nouveaux endpoints /api/auth/mfa/* - Frontend: QR code setup, 6-digit auto-submit, recovery codes - Settings: section Sécurité avec enable/disable MFA - CSS: mfa-challenge, setup-card, recovery-list, badges - i18n: 36 nouvelles clés EN/FR - pyotp ajouté aux dépendances - 30 tests (TOTP, recovery, API endpoints, login flow) - 439 tests passent au total
This commit is contained in:
@@ -2398,6 +2398,34 @@ select {
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
}
|
||||
/* Theme import/export actions */
|
||||
.theme-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
.theme-action-btn {
|
||||
padding: 5px 14px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-secondary);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
.theme-action-btn:hover {
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.theme-custom-badge {
|
||||
font-size: 0.6rem;
|
||||
color: var(--accent);
|
||||
margin-left: 2px;
|
||||
}
|
||||
/* Profile section */
|
||||
.profile-form { max-width: 420px; }
|
||||
.profile-field { margin-bottom: 14px; }
|
||||
@@ -8878,3 +8906,103 @@ body.popup-mode .content-area {
|
||||
right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── MFA (Multi-Factor Authentication) ────────────────────────────────── */
|
||||
|
||||
.mfa-challenge {
|
||||
margin-top: 20px;
|
||||
padding: 24px;
|
||||
border-radius: 12px;
|
||||
background: var(--surface, #1a1a2e);
|
||||
border: 1px solid var(--border, #333);
|
||||
text-align: center;
|
||||
}
|
||||
.mfa-challenge .mfa-icon { font-size: 48px; margin-bottom: 12px; }
|
||||
.mfa-challenge h3 { margin: 0 0 8px; color: var(--text, #fff); }
|
||||
.mfa-challenge p { color: var(--muted, #999); margin: 0 0 16px; }
|
||||
.mfa-code-input {
|
||||
width: 180px;
|
||||
font-size: 28px;
|
||||
text-align: center;
|
||||
letter-spacing: 12px;
|
||||
padding: 12px;
|
||||
border: 2px solid var(--border, #444);
|
||||
border-radius: 8px;
|
||||
background: var(--bg, #0d0d1a);
|
||||
color: var(--text, #fff);
|
||||
font-family: monospace;
|
||||
outline: none;
|
||||
}
|
||||
.mfa-code-input:focus { border-color: var(--accent, #7C3AED); }
|
||||
.mfa-code-input-group { display: flex; gap: 8px; justify-content: center; align-items: center; margin-bottom: 12px; }
|
||||
.mfa-code-input-group span { color: var(--muted, #999); font-size: 24px; }
|
||||
.mfa-link-btn {
|
||||
background: none; border: none; color: var(--accent, #7C3AED);
|
||||
cursor: pointer; font-size: 13px; text-decoration: underline;
|
||||
}
|
||||
.mfa-link-btn:hover { opacity: 0.8; }
|
||||
.mfa-error { color: #e74c3c; font-size: 13px; margin-top: 8px; }
|
||||
.mfa-recovery-input { width: 200px; font-size: 20px; letter-spacing: 4px; }
|
||||
|
||||
/* MFA Settings (Security tab) */
|
||||
.mfa-status-section { padding: 16px 0; }
|
||||
.mfa-status-row { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
|
||||
.mfa-badge {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 4px 12px; border-radius: 20px; font-size: 13px; font-weight: 600;
|
||||
}
|
||||
.mfa-badge-on { background: #1a3a2a; color: #4ade80; }
|
||||
.mfa-badge-off { background: #3a2a1a; color: #f59e0b; }
|
||||
.mfa-status-label { color: var(--text, #fff); font-weight: 500; }
|
||||
|
||||
/* MFA Setup card */
|
||||
.mfa-setup-card {
|
||||
padding: 20px; border-radius: 10px;
|
||||
background: var(--surface2, #1e1e3a); border: 1px solid var(--border, #333);
|
||||
}
|
||||
.mfa-setup-card h4 { margin: 0 0 12px; color: var(--text, #fff); }
|
||||
.mfa-qr-container { text-align: center; margin: 16px 0; }
|
||||
.mfa-qr-code img, .mfa-qr-code canvas { max-width: 200px; border-radius: 8px; }
|
||||
.mfa-secret-details { margin-top: 12px; }
|
||||
.mfa-secret-code {
|
||||
font-family: monospace; font-size: 14px; padding: 8px 12px;
|
||||
background: var(--bg, #0d0d1a); border-radius: 6px;
|
||||
color: var(--accent, #7C3AED); cursor: pointer; user-select: all;
|
||||
display: inline-block;
|
||||
}
|
||||
.mfa-info-text { color: var(--muted, #999); font-size: 13px; margin: 8px 0; }
|
||||
.mfa-subtitle { color: var(--muted, #999); font-size: 13px; margin: 0 0 16px; }
|
||||
.mfa-verify-section { display: flex; gap: 8px; align-items: center; margin-top: 12px; }
|
||||
.mfa-actions { margin-top: 16px; }
|
||||
|
||||
/* MFA Recovery codes display */
|
||||
.mfa-recovery-card {
|
||||
padding: 20px; border-radius: 10px;
|
||||
background: var(--surface2, #1e1e3a); border: 1px solid var(--border, #333);
|
||||
margin-top: 16px;
|
||||
}
|
||||
.mfa-recovery-card h4 { margin: 0 0 8px; color: var(--text, #fff); }
|
||||
.mfa-recovery-list {
|
||||
display: grid; grid-template-columns: 1fr 1fr; gap: 6px 24px;
|
||||
margin: 12px 0; list-style: none; padding: 0;
|
||||
}
|
||||
.mfa-recovery-code {
|
||||
font-family: monospace; font-size: 14px; padding: 4px 8px;
|
||||
background: var(--bg, #0d0d1a); border-radius: 4px;
|
||||
color: var(--text, #fff); letter-spacing: 1px;
|
||||
}
|
||||
.mfa-recovery-actions { display: flex; gap: 8px; margin-top: 12px; }
|
||||
|
||||
/* MFA Disable card */
|
||||
.mfa-disable-card {
|
||||
padding: 16px; border-radius: 10px;
|
||||
background: var(--surface2, #1e1e3a); border: 1px solid #e74c3c33;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.mfa-disable-card h4 { margin: 0 0 8px; color: #e74c3c; }
|
||||
.mfa-disable-actions { display: flex; gap: 8px; margin-top: 12px; }
|
||||
|
||||
.mfa-warning {
|
||||
color: #f59e0b; font-size: 13px; padding: 8px 12px;
|
||||
background: #3a2a1a; border-radius: 6px; margin-top: 8px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user