feat(06.2): log attempted email on failed login and surface it in audit log

- auth.py: store attempted_email in metadata_ and link user_id when the account exists (wrong password case); previously logged no PII at all
- AuditLogTab: Email column falls back to metadata_.attempted_email in amber with "(attempted)" label when no confirmed user_email is available

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-06-01 21:02:37 +02:00
parent 7027347597
commit 2686fde2d7
2 changed files with 8 additions and 5 deletions
+3 -4
View File
@@ -232,15 +232,14 @@ async def login(
# Verify password (anti-enumeration: same error regardless of whether user exists)
if user is None or not auth_service.verify_password(body.password, user.password_hash):
# D-13: log login failure WITHOUT PII (no email, no password) — T-04-07-01
await write_audit_log(
session,
event_type="auth.login_failed",
user_id=None,
actor_id=None,
user_id=user.id if user else None,
actor_id=user.id if user else None,
resource_id=None,
ip_address=_ip,
metadata_=None,
metadata_={"attempted_email": str(body.email)},
)
await session.commit()
raise HTTPException(
@@ -109,7 +109,11 @@
>
<td class="px-4 py-3 font-mono text-xs text-gray-500">{{ formatTimestamp(entry.created_at) }}</td>
<td class="px-4 py-3 text-sm text-gray-700">{{ entry.user_handle || entry.user_id || '—' }}</td>
<td class="px-4 py-3 text-sm text-gray-500">{{ entry.user_email || '—' }}</td>
<td class="px-4 py-3 text-sm text-gray-500">
<span v-if="entry.user_email">{{ entry.user_email }}</span>
<span v-else-if="entry.metadata_?.attempted_email" class="text-amber-600">{{ entry.metadata_.attempted_email }} <span class="text-xs">(attempted)</span></span>
<span v-else></span>
</td>
<td class="px-4 py-3">
<span
class="text-xs px-2 py-1 rounded-full font-medium"