Repository: goauthentik/authentik

Issue:
Forward Auth requests using Basic Auth are missing information in user-specific headers
**Describe the bug**
Forward Auth requests with the Proxy Provider that use Basic Auth have user-specific headers that have no value assigned in the request to the application after authenticating. The headers I found that are missing values include `X-Authentik-Username`, `X-Authentik-Groups`, `X-Authentik-Name`, and `X-Authentik-Email`.

**To Reproduce**
Steps to reproduce the behavior:

1. Deploy authentik with traefik configured for forward auth
2. Deploy whoami container (traefik/whoami) and configure traefik labels to proxy to the application and perform authentication
3. In authentik, configure a proxy provider with forward auth for a single application for whoami, assign it to an application and add it to the default outpost
4. Test authentication to the whoami application with normal auth workflow in the browser. The response body from the whoami application to the browser prints the headers and their values it found on the request, including the ones added by authentik. Note the headers like `X-Authentik-Username` and `X-Authentik-Groups` are populated correctly.
5. Create an app password for the same user used for the previous test. Keep in mind the app passwords have an lifetime of 30 minutes, but that's a separate issue: #5881
6. Use Postman or curl to send a GET request to the whoami application using basic auth with the username and app password. Note this time, whoami indicates the headers `X-Authentik-Username`, `X-Authentik-Groups`, `X-Authentik-Name`, and `X-Authentik-Email` are present, however they have no values assigned.

**Expected behavior**
Headers added by authentik including `X-Authentik-Username`, `X-Authentik-Groups`, `X-Authentik-Name`, and `X-Authentik-Email` should be present and contain correct information in the request to whoami after traefik authenticates it with authentik. This should be seen in the response from the whoami application.

**Screenshots**
N/A

**Logs**
Here is the response from whoami application after authenticating using an app password in Postman, with sensitive bits redacted:
```
Hostname: ed3d7d075baa
IP: 127.0.0.1
IP: [whoami internal IP on docker network]
RemoteAddr: [traefik internal IP on docker network]
GET / HTTP/1.1
Host: whoami.[domain name]
User-Agent: PostmanRuntime/7.32.3
Accept: */*
Accept-Encoding: gzip, deflate, br
Cache-Control: no-cache
Cookie: authentik_proxy_AwAwFebs=[redacted]
Postman-Token: c0f66276-fd82-431c-9148-db823ab3c37a
X-Authentik-Email: 
X-Authentik-Groups: 
X-Authentik-Jwt: [redacted]
X-Authentik-Meta-App: whoami
X-Authentik-Meta-Jwks: http://localhost:8000/application/o/whoami/jwks/
X-Authentik-Meta-Outpost: authentik Embedded Outpost
X-Authentik-Meta-Provider: whoami
X-Authentik-Meta-Version: goauthentik.io/outpost/2023.6.1
X-Authentik-Name: 
X-Authentik-Uid: [redacted]
X-Authentik-Username: 
X-Forwarded-For: [original client IP]
X-Forwarded-Host: whoami.[domain name]
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: be921168fe37
X-Real-Ip: [original client IP]
```

**Version and Deployment (please complete the following information):**

-   authentik version: 2023.6.1
-   traefik version: 2.10.4
-   Deployment: docker-compose

**Additional context**
- I noticed that one header `X-Authentik-Uid` does have the correct value for the user that is authenticating with basic auth. So authentik is aware of the user signing in, just for some reason the other user-specific headers are not assigned any values.
- I was unsuccessful in testing this using Tokens instead of App Passwords, so it may be worth checking if the issue is also present on requests authenticating with Tokens.


Discussion:
I updated the title/description after posting this to indicate this has more to do with basic auth than app passwords. I tested after posting this and found that the headers are correctly populated when using app password to sign in using the normal auth workflow in the browser.

Resolving patch:
diff --git a/authentik/providers/oauth2/models.py b/authentik/providers/oauth2/models.py
--- a/authentik/providers/oauth2/models.py
+++ b/authentik/providers/oauth2/models.py
@@ -2,6 +2,7 @@
 import base64
 import binascii
 import json
+from dataclasses import asdict
 from functools import cached_property
 from hashlib import sha256
 from typing import Any, Optional
@@ -358,7 +359,7 @@ def id_token(self) -> IDToken:
     @id_token.setter
     def id_token(self, value: IDToken):
         self.token = value.to_access_token(self.provider)
-        self._id_token = json.dumps(value.to_dict())
+        self._id_token = json.dumps(asdict(value))
 
     @property
     def at_hash(self):
@@ -400,7 +401,7 @@ def id_token(self) -> IDToken:
 
     @id_token.setter
     def id_token(self, value: IDToken):
-        self._id_token = json.dumps(value.to_dict())
+        self._id_token = json.dumps(asdict(value))
 
     @property
     def serializer(self) -> Serializer:
diff --git a/scripts/generate_config.py b/scripts/generate_config.py
--- a/scripts/generate_config.py
+++ b/scripts/generate_config.py
@@ -16,9 +16,6 @@
                 "container_image_base": "ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s",
             },
             "blueprints_dir": "./blueprints",
-            "web": {
-                "outpost_port_offset": 100,
-            },
             "cert_discovery_dir": "./certs",
             "geoip": "tests/GeoLite2-City-Test.mmdb",
         },


Test patch:
diff --git a/authentik/providers/oauth2/tests/test_token_cc.py b/authentik/providers/oauth2/tests/test_token_cc.py
--- a/authentik/providers/oauth2/tests/test_token_cc.py
+++ b/authentik/providers/oauth2/tests/test_token_cc.py
@@ -151,6 +151,14 @@ def test_successful(self):
         )
         self.assertEqual(jwt["given_name"], self.user.name)
         self.assertEqual(jwt["preferred_username"], self.user.username)
+        jwt = decode(
+            body["id_token"],
+            key=self.provider.signing_key.public_key,
+            algorithms=[alg],
+            audience=self.provider.client_id,
+        )
+        self.assertEqual(jwt["given_name"], self.user.name)
+        self.assertEqual(jwt["preferred_username"], self.user.username)
 
     def test_successful_password(self):
         """test successful (password grant)"""