We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Date field can't be serialized to JSON on user write stage
**Describe the bug**

`Date` fields can't be serialised to JSON and thus are not saved to the user.

**To Reproduce**

I've added a field of type `Date` to my user settings by adding it in the `default-user-settings` prompt stage.
When I go into my user settings, set a value and save it, my user's fields are no longer displayed, instead replaced by a `Open settings` button.

![image](https://github.com/goauthentik/authentik/assets/40198445/d6945e36-424d-4678-9bec-8b4b82c4614a)

When I click the button I get this error message:

```
builtins.TypeError: Object of type date is not JSON serializable
```

![image](https://github.com/goauthentik/authentik/assets/40198445/41867936-781a-44e2-a941-1a205320a9ef)

-   authentik version:  2023.6.1
-   Deployment: docker-compose

**Additional context**
https://discord.com/channels/809154715984199690/1129892642080161913


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `authentik/events/utils.py`
Content:
```
1 """event utilities"""
2 import re
3 from copy import copy
4 from dataclasses import asdict, is_dataclass
5 from enum import Enum
6 from pathlib import Path
7 from types import GeneratorType
8 from typing import Any, Optional
9 from uuid import UUID
10 
11 from django.contrib.auth.models import AnonymousUser
12 from django.core.handlers.wsgi import WSGIRequest
13 from django.db import models
14 from django.db.models.base import Model
15 from django.http.request import HttpRequest
16 from django.views.debug import SafeExceptionReporterFilter
17 from geoip2.models import City
18 from guardian.utils import get_anonymous_user
19 
20 from authentik.blueprints.v1.common import YAMLTag
21 from authentik.core.models import User
22 from authentik.events.geo import GEOIP_READER
23 from authentik.policies.types import PolicyRequest
24 
25 # Special keys which are *not* cleaned, even when the default filter
26 # is matched
27 ALLOWED_SPECIAL_KEYS = re.compile("passing", flags=re.I)
28 
29 
30 def cleanse_item(key: str, value: Any) -> Any:
31     """Cleanse a single item"""
32     if isinstance(value, dict):
33         return cleanse_dict(value)
34     if isinstance(value, (list, tuple, set)):
35         for idx, item in enumerate(value):
36             value[idx] = cleanse_item(key, item)
37         return value
38     try:
39         if SafeExceptionReporterFilter.hidden_settings.search(
40             key
41         ) and not ALLOWED_SPECIAL_KEYS.search(key):
42             return SafeExceptionReporterFilter.cleansed_substitute
43     except TypeError:  # pragma: no cover
44         return value
45     return value
46 
47 
48 def cleanse_dict(source: dict[Any, Any]) -> dict[Any, Any]:
49     """Cleanse a dictionary, recursively"""
50     final_dict = {}
51     for key, value in source.items():
52         new_value = cleanse_item(key, value)
53         if new_value is not ...:
54             final_dict[key] = new_value
55     return final_dict
56 
57 
58 def model_to_dict(model: Model) -> dict[str, Any]:
59     """Convert model to dict"""
60     name = str(model)
61     if hasattr(model, "name"):
62         name = model.name
63     return {
64         "app": model._meta.app_label,
65         "model_name": model._meta.model_name,
66         "pk": model.pk,
67         "name": name,
68     }
69 
70 
71 def get_user(user: User, original_user: Optional[User] = None) -> dict[str, Any]:
72     """Convert user object to dictionary, optionally including the original user"""
73     if isinstance(user, AnonymousUser):
74         user = get_anonymous_user()
75     user_data = {
76         "username": user.username,
77         "pk": user.pk,
78         "email": user.email,
79     }
80     if original_user:
81         original_data = get_user(original_user)
82         original_data["on_behalf_of"] = user_data
83         return original_data
84     return user_data
85 
86 
87 # pylint: disable=too-many-return-statements
88 def sanitize_item(value: Any) -> Any:
89     """Sanitize a single item, ensure it is JSON parsable"""
90     if is_dataclass(value):
91         # Because asdict calls `copy.deepcopy(obj)` on everything that's not tuple/dict,
92         # and deepcopy doesn't work with HttpRequest (neither django nor rest_framework).
93         # (more specifically doesn't work with ResolverMatch)
94         # rest_framework's custom Request class makes this more complicated as it also holds a
95         # thread lock.
96         # Since this class is mainly used for Events which already hold the http request context
97         # we just remove the http_request from the shallow policy request
98         # Currently, the only dataclass that actually holds an http request is a PolicyRequest
99         if isinstance(value, PolicyRequest) and value.http_request is not None:
100             value: PolicyRequest = copy(value)
101             value.http_request = None
102         value = asdict(value)
103     if isinstance(value, dict):
104         return sanitize_dict(value)
105     if isinstance(value, GeneratorType):
106         return sanitize_item(list(value))
107     if isinstance(value, (list, tuple, set)):
108         new_values = []
109         for item in value:
110             new_value = sanitize_item(item)
111             if new_value:
112                 new_values.append(new_value)
113         return new_values
114     if isinstance(value, (User, AnonymousUser)):
115         return sanitize_dict(get_user(value))
116     if isinstance(value, models.Model):
117         return sanitize_dict(model_to_dict(value))
118     if isinstance(value, UUID):
119         return value.hex
120     if isinstance(value, (HttpRequest, WSGIRequest)):
121         return ...
122     if isinstance(value, City):
123         return GEOIP_READER.city_to_dict(value)
124     if isinstance(value, Path):
125         return str(value)
126     if isinstance(value, Exception):
127         return str(value)
128     if isinstance(value, YAMLTag):
129         return str(value)
130     if isinstance(value, Enum):
131         return value.value
132     if isinstance(value, type):
133         return {
134             "type": value.__name__,
135             "module": value.__module__,
136         }
137     return value
138 
139 
140 def sanitize_dict(source: dict[Any, Any]) -> dict[Any, Any]:
141     """clean source of all Models that would interfere with the JSONField.
142     Models are replaced with a dictionary of {
143         app: str,
144         name: str,
145         pk: Any
146     }"""
147     final_dict = {}
148     for key, value in source.items():
149         new_value = sanitize_item(value)
150         if new_value is not ...:
151             final_dict[key] = new_value
152     return final_dict
153
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/authentik/events/utils.py b/authentik/events/utils.py
--- a/authentik/events/utils.py
+++ b/authentik/events/utils.py
@@ -2,6 +2,7 @@
 import re
 from copy import copy
 from dataclasses import asdict, is_dataclass
+from datetime import date, datetime, time, timedelta
 from enum import Enum
 from pathlib import Path
 from types import GeneratorType
@@ -13,6 +14,7 @@
 from django.db import models
 from django.db.models.base import Model
 from django.http.request import HttpRequest
+from django.utils import timezone
 from django.views.debug import SafeExceptionReporterFilter
 from geoip2.models import City
 from guardian.utils import get_anonymous_user
@@ -84,7 +86,7 @@
     return user_data
 
 
-# pylint: disable=too-many-return-statements
+# pylint: disable=too-many-return-statements,too-many-branches
 def sanitize_item(value: Any) -> Any:
     """Sanitize a single item, ensure it is JSON parsable"""
     if is_dataclass(value):
@@ -134,6 +136,23 @@
             "type": value.__name__,
             "module": value.__module__,
         }
+    # See
+    # https://github.com/encode/django-rest-framework/blob/master/rest_framework/utils/encoders.py
+    # For Date Time string spec, see ECMA 262
+    # https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
+    if isinstance(value, datetime):
+        representation = value.isoformat()
+        if representation.endswith("+00:00"):
+            representation = representation[:-6] + "Z"
+        return representation
+    if isinstance(value, date):
+        return value.isoformat()
+    if isinstance(value, time):
+        if timezone and timezone.is_aware(value):
+            raise ValueError("JSON can't represent timezone-aware times.")
+        return value.isoformat()
+    if isinstance(value, timedelta):
+        return str(value.total_seconds())
     return value

{"golden_diff": "diff --git a/authentik/events/utils.py b/authentik/events/utils.py\n--- a/authentik/events/utils.py\n+++ b/authentik/events/utils.py\n@@ -2,6 +2,7 @@\n import re\n from copy import copy\n from dataclasses import asdict, is_dataclass\n+from datetime import date, datetime, time, timedelta\n from enum import Enum\n from pathlib import Path\n from types import GeneratorType\n@@ -13,6 +14,7 @@\n from django.db import models\n from django.db.models.base import Model\n from django.http.request import HttpRequest\n+from django.utils import timezone\n from django.views.debug import SafeExceptionReporterFilter\n from geoip2.models import City\n from guardian.utils import get_anonymous_user\n@@ -84,7 +86,7 @@\n     return user_data\n \n \n-# pylint: disable=too-many-return-statements\n+# pylint: disable=too-many-return-statements,too-many-branches\n def sanitize_item(value: Any) -> Any:\n     \"\"\"Sanitize a single item, ensure it is JSON parsable\"\"\"\n     if is_dataclass(value):\n@@ -134,6 +136,23 @@\n             \"type\": value.__name__,\n             \"module\": value.__module__,\n         }\n+    # See\n+    # https://github.com/encode/django-rest-framework/blob/master/rest_framework/utils/encoders.py\n+    # For Date Time string spec, see ECMA 262\n+    # https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15\n+    if isinstance(value, datetime):\n+        representation = value.isoformat()\n+        if representation.endswith(\"+00:00\"):\n+            representation = representation[:-6] + \"Z\"\n+        return representation\n+    if isinstance(value, date):\n+        return value.isoformat()\n+    if isinstance(value, time):\n+        if timezone and timezone.is_aware(value):\n+            raise ValueError(\"JSON can't represent timezone-aware times.\")\n+        return value.isoformat()\n+    if isinstance(value, timedelta):\n+        return str(value.total_seconds())\n     return value\n", "issue": "Date field can't be serialized to JSON on user write stage\n**Describe the bug**\r\n\r\n`Date` fields can't be serialised to JSON and thus are not saved to the user.\r\n\r\n**To Reproduce**\r\n\r\nI've added a field of type `Date` to my user settings by adding it in the `default-user-settings` prompt stage.\r\nWhen I go into my user settings, set a value and save it, my user's fields are no longer displayed, instead replaced by a `Open settings` button.\r\n\r\n![image](https://github.com/goauthentik/authentik/assets/40198445/d6945e36-424d-4678-9bec-8b4b82c4614a)\r\n\r\nWhen I click the button I get this error message:\r\n\r\n```\r\nbuiltins.TypeError: Object of type date is not JSON serializable\r\n```\r\n\r\n![image](https://github.com/goauthentik/authentik/assets/40198445/41867936-781a-44e2-a941-1a205320a9ef)\r\n\r\n-   authentik version:  2023.6.1\r\n-   Deployment: docker-compose\r\n\r\n**Additional context**\r\nhttps://discord.com/channels/809154715984199690/1129892642080161913\r\n\n", "before_files": [{"content": "\"\"\"event utilities\"\"\"\nimport re\nfrom copy import copy\nfrom dataclasses import asdict, is_dataclass\nfrom enum import Enum\nfrom pathlib import Path\nfrom types import GeneratorType\nfrom typing import Any, Optional\nfrom uuid import UUID\n\nfrom django.contrib.auth.models import AnonymousUser\nfrom django.core.handlers.wsgi import WSGIRequest\nfrom django.db import models\nfrom django.db.models.base import Model\nfrom django.http.request import HttpRequest\nfrom django.views.debug import SafeExceptionReporterFilter\nfrom geoip2.models import City\nfrom guardian.utils import get_anonymous_user\n\nfrom authentik.blueprints.v1.common import YAMLTag\nfrom authentik.core.models import User\nfrom authentik.events.geo import GEOIP_READER\nfrom authentik.policies.types import PolicyRequest\n\n# Special keys which are *not* cleaned, even when the default filter\n# is matched\nALLOWED_SPECIAL_KEYS = re.compile(\"passing\", flags=re.I)\n\n\ndef cleanse_item(key: str, value: Any) -> Any:\n    \"\"\"Cleanse a single item\"\"\"\n    if isinstance(value, dict):\n        return cleanse_dict(value)\n    if isinstance(value, (list, tuple, set)):\n        for idx, item in enumerate(value):\n            value[idx] = cleanse_item(key, item)\n        return value\n    try:\n        if SafeExceptionReporterFilter.hidden_settings.search(\n            key\n        ) and not ALLOWED_SPECIAL_KEYS.search(key):\n            return SafeExceptionReporterFilter.cleansed_substitute\n    except TypeError:  # pragma: no cover\n        return value\n    return value\n\n\ndef cleanse_dict(source: dict[Any, Any]) -> dict[Any, Any]:\n    \"\"\"Cleanse a dictionary, recursively\"\"\"\n    final_dict = {}\n    for key, value in source.items():\n        new_value = cleanse_item(key, value)\n        if new_value is not ...:\n            final_dict[key] = new_value\n    return final_dict\n\n\ndef model_to_dict(model: Model) -> dict[str, Any]:\n    \"\"\"Convert model to dict\"\"\"\n    name = str(model)\n    if hasattr(model, \"name\"):\n        name = model.name\n    return {\n        \"app\": model._meta.app_label,\n        \"model_name\": model._meta.model_name,\n        \"pk\": model.pk,\n        \"name\": name,\n    }\n\n\ndef get_user(user: User, original_user: Optional[User] = None) -> dict[str, Any]:\n    \"\"\"Convert user object to dictionary, optionally including the original user\"\"\"\n    if isinstance(user, AnonymousUser):\n        user = get_anonymous_user()\n    user_data = {\n        \"username\": user.username,\n        \"pk\": user.pk,\n        \"email\": user.email,\n    }\n    if original_user:\n        original_data = get_user(original_user)\n        original_data[\"on_behalf_of\"] = user_data\n        return original_data\n    return user_data\n\n\n# pylint: disable=too-many-return-statements\ndef sanitize_item(value: Any) -> Any:\n    \"\"\"Sanitize a single item, ensure it is JSON parsable\"\"\"\n    if is_dataclass(value):\n        # Because asdict calls `copy.deepcopy(obj)` on everything that's not tuple/dict,\n        # and deepcopy doesn't work with HttpRequest (neither django nor rest_framework).\n        # (more specifically doesn't work with ResolverMatch)\n        # rest_framework's custom Request class makes this more complicated as it also holds a\n        # thread lock.\n        # Since this class is mainly used for Events which already hold the http request context\n        # we just remove the http_request from the shallow policy request\n        # Currently, the only dataclass that actually holds an http request is a PolicyRequest\n        if isinstance(value, PolicyRequest) and value.http_request is not None:\n            value: PolicyRequest = copy(value)\n            value.http_request = None\n        value = asdict(value)\n    if isinstance(value, dict):\n        return sanitize_dict(value)\n    if isinstance(value, GeneratorType):\n        return sanitize_item(list(value))\n    if isinstance(value, (list, tuple, set)):\n        new_values = []\n        for item in value:\n            new_value = sanitize_item(item)\n            if new_value:\n                new_values.append(new_value)\n        return new_values\n    if isinstance(value, (User, AnonymousUser)):\n        return sanitize_dict(get_user(value))\n    if isinstance(value, models.Model):\n        return sanitize_dict(model_to_dict(value))\n    if isinstance(value, UUID):\n        return value.hex\n    if isinstance(value, (HttpRequest, WSGIRequest)):\n        return ...\n    if isinstance(value, City):\n        return GEOIP_READER.city_to_dict(value)\n    if isinstance(value, Path):\n        return str(value)\n    if isinstance(value, Exception):\n        return str(value)\n    if isinstance(value, YAMLTag):\n        return str(value)\n    if isinstance(value, Enum):\n        return value.value\n    if isinstance(value, type):\n        return {\n            \"type\": value.__name__,\n            \"module\": value.__module__,\n        }\n    return value\n\n\ndef sanitize_dict(source: dict[Any, Any]) -> dict[Any, Any]:\n    \"\"\"clean source of all Models that would interfere with the JSONField.\n    Models are replaced with a dictionary of {\n        app: str,\n        name: str,\n        pk: Any\n    }\"\"\"\n    final_dict = {}\n    for key, value in source.items():\n        new_value = sanitize_item(value)\n        if new_value is not ...:\n            final_dict[key] = new_value\n    return final_dict\n", "path": "authentik/events/utils.py"}], "after_files": [{"content": "\"\"\"event utilities\"\"\"\nimport re\nfrom copy import copy\nfrom dataclasses import asdict, is_dataclass\nfrom datetime import date, datetime, time, timedelta\nfrom enum import Enum\nfrom pathlib import Path\nfrom types import GeneratorType\nfrom typing import Any, Optional\nfrom uuid import UUID\n\nfrom django.contrib.auth.models import AnonymousUser\nfrom django.core.handlers.wsgi import WSGIRequest\nfrom django.db import models\nfrom django.db.models.base import Model\nfrom django.http.request import HttpRequest\nfrom django.utils import timezone\nfrom django.views.debug import SafeExceptionReporterFilter\nfrom geoip2.models import City\nfrom guardian.utils import get_anonymous_user\n\nfrom authentik.blueprints.v1.common import YAMLTag\nfrom authentik.core.models import User\nfrom authentik.events.geo import GEOIP_READER\nfrom authentik.policies.types import PolicyRequest\n\n# Special keys which are *not* cleaned, even when the default filter\n# is matched\nALLOWED_SPECIAL_KEYS = re.compile(\"passing\", flags=re.I)\n\n\ndef cleanse_item(key: str, value: Any) -> Any:\n    \"\"\"Cleanse a single item\"\"\"\n    if isinstance(value, dict):\n        return cleanse_dict(value)\n    if isinstance(value, (list, tuple, set)):\n        for idx, item in enumerate(value):\n            value[idx] = cleanse_item(key, item)\n        return value\n    try:\n        if SafeExceptionReporterFilter.hidden_settings.search(\n            key\n        ) and not ALLOWED_SPECIAL_KEYS.search(key):\n            return SafeExceptionReporterFilter.cleansed_substitute\n    except TypeError:  # pragma: no cover\n        return value\n    return value\n\n\ndef cleanse_dict(source: dict[Any, Any]) -> dict[Any, Any]:\n    \"\"\"Cleanse a dictionary, recursively\"\"\"\n    final_dict = {}\n    for key, value in source.items():\n        new_value = cleanse_item(key, value)\n        if new_value is not ...:\n            final_dict[key] = new_value\n    return final_dict\n\n\ndef model_to_dict(model: Model) -> dict[str, Any]:\n    \"\"\"Convert model to dict\"\"\"\n    name = str(model)\n    if hasattr(model, \"name\"):\n        name = model.name\n    return {\n        \"app\": model._meta.app_label,\n        \"model_name\": model._meta.model_name,\n        \"pk\": model.pk,\n        \"name\": name,\n    }\n\n\ndef get_user(user: User, original_user: Optional[User] = None) -> dict[str, Any]:\n    \"\"\"Convert user object to dictionary, optionally including the original user\"\"\"\n    if isinstance(user, AnonymousUser):\n        user = get_anonymous_user()\n    user_data = {\n        \"username\": user.username,\n        \"pk\": user.pk,\n        \"email\": user.email,\n    }\n    if original_user:\n        original_data = get_user(original_user)\n        original_data[\"on_behalf_of\"] = user_data\n        return original_data\n    return user_data\n\n\n# pylint: disable=too-many-return-statements,too-many-branches\ndef sanitize_item(value: Any) -> Any:\n    \"\"\"Sanitize a single item, ensure it is JSON parsable\"\"\"\n    if is_dataclass(value):\n        # Because asdict calls `copy.deepcopy(obj)` on everything that's not tuple/dict,\n        # and deepcopy doesn't work with HttpRequest (neither django nor rest_framework).\n        # (more specifically doesn't work with ResolverMatch)\n        # rest_framework's custom Request class makes this more complicated as it also holds a\n        # thread lock.\n        # Since this class is mainly used for Events which already hold the http request context\n        # we just remove the http_request from the shallow policy request\n        # Currently, the only dataclass that actually holds an http request is a PolicyRequest\n        if isinstance(value, PolicyRequest) and value.http_request is not None:\n            value: PolicyRequest = copy(value)\n            value.http_request = None\n        value = asdict(value)\n    if isinstance(value, dict):\n        return sanitize_dict(value)\n    if isinstance(value, GeneratorType):\n        return sanitize_item(list(value))\n    if isinstance(value, (list, tuple, set)):\n        new_values = []\n        for item in value:\n            new_value = sanitize_item(item)\n            if new_value:\n                new_values.append(new_value)\n        return new_values\n    if isinstance(value, (User, AnonymousUser)):\n        return sanitize_dict(get_user(value))\n    if isinstance(value, models.Model):\n        return sanitize_dict(model_to_dict(value))\n    if isinstance(value, UUID):\n        return value.hex\n    if isinstance(value, (HttpRequest, WSGIRequest)):\n        return ...\n    if isinstance(value, City):\n        return GEOIP_READER.city_to_dict(value)\n    if isinstance(value, Path):\n        return str(value)\n    if isinstance(value, Exception):\n        return str(value)\n    if isinstance(value, YAMLTag):\n        return str(value)\n    if isinstance(value, Enum):\n        return value.value\n    if isinstance(value, type):\n        return {\n            \"type\": value.__name__,\n            \"module\": value.__module__,\n        }\n    # See\n    # https://github.com/encode/django-rest-framework/blob/master/rest_framework/utils/encoders.py\n    # For Date Time string spec, see ECMA 262\n    # https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15\n    if isinstance(value, datetime):\n        representation = value.isoformat()\n        if representation.endswith(\"+00:00\"):\n            representation = representation[:-6] + \"Z\"\n        return representation\n    if isinstance(value, date):\n        return value.isoformat()\n    if isinstance(value, time):\n        if timezone and timezone.is_aware(value):\n            raise ValueError(\"JSON can't represent timezone-aware times.\")\n        return value.isoformat()\n    if isinstance(value, timedelta):\n        return str(value.total_seconds())\n    return value\n\n\ndef sanitize_dict(source: dict[Any, Any]) -> dict[Any, Any]:\n    \"\"\"clean source of all Models that would interfere with the JSONField.\n    Models are replaced with a dictionary of {\n        app: str,\n        name: str,\n        pk: Any\n    }\"\"\"\n    final_dict = {}\n    for key, value in source.items():\n        new_value = sanitize_item(value)\n        if new_value is not ...:\n            final_dict[key] = new_value\n    return final_dict\n", "path": "authentik/events/utils.py"}]}