Repository: goauthentik/authentik

Issue:
added user_agent to Failed Logins Events
Including the User-Agent in failed login attempt logs improves security monitoring. It enables the identification of suspicious or potentially malicious login attempts originating from specific devices or browsers.


Discussion:
### <span aria-hidden="true">✅</span> Deploy Preview for *authentik-storybook* canceled.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 049d2e1ba3e1ab8df6f36e8b8122584ea3b68003 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/authentik-storybook/deploys/655cf0b5f4493700088a87a9 |
## [Codecov](https://app.codecov.io/gh/goauthentik/authentik/pull/7664?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goauthentik) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`b7055c5`)](https://app.codecov.io/gh/goauthentik/authentik/commit/b7055c583842f69f46f31a53c6b2fbc1fba17905?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goauthentik) 92.60% compared to head [(`049d2e1`)](https://app.codecov.io/gh/goauthentik/authentik/pull/7664?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goauthentik) 52.33%.
> Report is 3 commits behind head on main.


<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##             main    #7664       +/-   ##
===========================================
- Coverage   92.60%   52.33%   -40.27%     
===========================================
  Files         587      588        +1     
  Lines       29075    29152       +77     
===========================================
- Hits        26925    15258    -11667     
- Misses       2150    13894    +11744     
```

| [Flag](https://app.codecov.io/gh/goauthentik/authentik/pull/7664/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goauthentik) | Coverage Δ | |
|---|---|---|
| [e2e](https://app.codecov.io/gh/goauthentik/authentik/pull/7664/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goauthentik) | `50.74% <100.00%> (-0.18%)` | :arrow_down: |
| [integration](https://app.codecov.io/gh/goauthentik/authentik/pull/7664/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goauthentik) | `25.92% <0.00%> (-0.07%)` | :arrow_down: |
| [unit](https://app.codecov.io/gh/goauthentik/authentik/pull/7664/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goauthentik) | `?` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goauthentik#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/goauthentik/authentik/pull/7664?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goauthentik).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goauthentik).

### <span aria-hidden="true">✅</span> Deploy Preview for *authentik* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 0286d5318a0b364b79508ecfde33fffd12582b56 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/authentik/deploys/655cee790d741800088a420e |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-7664--authentik.netlify.app](https://deploy-preview-7664--authentik.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTc2NjQtLWF1dGhlbnRpay5uZXRsaWZ5LmFwcCJ9.OUfYtfDEnE4JDeH3AEJl8TB3ev4nr46ui_Uury2mLxM)<br /><br />_Use your smartphone camera to open QR code link._</details> |
|<span aria-hidden="true">![Lighthouse](https://app.netlify.com/public/external-usage/git-comments/lighthouse-sm.png)</span><br />Lighthouse | 1 paths audited<br />**Performance**: 96 (no change from production)<br />**Accessibility**: 90 (no change from production)<br />**Best Practices**: 100 (no change from production)<br />**SEO**: 80 (no change from production)<br />**PWA**: -<br />[View the detailed breakdown and full score reports](https://app.netlify.com/sites/authentik/deploys/655cee790d741800088a420e) |
---

_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/authentik/configuration/deploys#deploy-notifications)._
Thanks for the PR! Including the user-agent makes sense, although imo it might as well be included in all events created from HTTP requests. For the Client IP, that is already included in events in a separate field (`client_ip`)

Resolving patch:
diff --git a/authentik/events/models.py b/authentik/events/models.py
--- a/authentik/events/models.py
+++ b/authentik/events/models.py
@@ -217,6 +217,7 @@ def from_http(self, request: HttpRequest, user: Optional[User] = None) -> "Event
                 "path": request.path,
                 "method": request.method,
                 "args": cleanse_dict(QueryDict(request.META.get("QUERY_STRING", ""))),
+                "user_agent": request.META.get("HTTP_USER_AGENT", ""),
             }
             # Special case for events created during flow execution
             # since they keep the http query within a wrapped query


Test patch:
diff --git a/authentik/events/tests/test_event.py b/authentik/events/tests/test_event.py
--- a/authentik/events/tests/test_event.py
+++ b/authentik/events/tests/test_event.py
@@ -53,7 +53,15 @@ def test_from_http_basic(self):
         """Test plain from_http"""
         event = Event.new("unittest").from_http(self.factory.get("/"))
         self.assertEqual(
-            event.context, {"http_request": {"args": {}, "method": "GET", "path": "/"}}
+            event.context,
+            {
+                "http_request": {
+                    "args": {},
+                    "method": "GET",
+                    "path": "/",
+                    "user_agent": "",
+                }
+            },
         )
 
     def test_from_http_clean_querystring(self):
@@ -67,6 +75,7 @@ def test_from_http_clean_querystring(self):
                     "args": {"token": SafeExceptionReporterFilter.cleansed_substitute},
                     "method": "GET",
                     "path": "/",
+                    "user_agent": "",
                 }
             },
         )
@@ -83,6 +92,7 @@ def test_from_http_clean_querystring_flow(self):
                     "args": {"token": SafeExceptionReporterFilter.cleansed_substitute},
                     "method": "GET",
                     "path": "/",
+                    "user_agent": "",
                 }
             },
         )
diff --git a/authentik/stages/authenticator_validate/tests/test_duo.py b/authentik/stages/authenticator_validate/tests/test_duo.py
--- a/authentik/stages/authenticator_validate/tests/test_duo.py
+++ b/authentik/stages/authenticator_validate/tests/test_duo.py
@@ -184,6 +184,7 @@ def test_full(self):
                     "args": {},
                     "method": "GET",
                     "path": f"/api/v3/flows/executor/{flow.slug}/",
+                    "user_agent": "",
                 },
             },
         )