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:
+3
-4
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user