Repository: goauthentik/authentik

Issue:
sending recovery email fails -- `Object of type MIMEImage is not JSON serializable`
**Describe the bug**
Fails both from the admin UI using the `Send Recovery Link` button or via the `Forgot password` self-service recovery flow.

**To Reproduce**

1. Attempt to send a recovery email
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
From the admin UI:
![image](https://github.com/goauthentik/authentik/assets/200575/f1078e90-5704-4911-9b8b-363940dee057)
Attempting to do it using recovery flow:
![image](https://github.com/goauthentik/authentik/assets/200575/d93632b2-e005-4fe0-b35e-8b8a17fc76fc)

**Logs**

<details>
    <summary>Stacktrace from authentik</summary>

```
Traceback (most recent call last):
  File "/authentik/flows/views/executor.py", line 287, in get
    stage_response = self.current_stage_view.dispatch(request)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/ak-root/venv/lib/python3.11/site-packages/django/views/generic/base.py", line 143, in dispatch
    return handler(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/authentik/stages/email/stage.py", line 142, in get
    self.send_email()
  File "/authentik/stages/email/stage.py", line 121, in send_email
    send_mails(current_stage, message)
  File "/authentik/stages/email/tasks.py", line 26, in send_mails
    promise = lazy_group()
              ^^^^^^^^^^^^
  File "/ak-root/venv/lib/python3.11/site-packages/celery/canvas.py", line 1568, in __call__
    return self.apply_async(partial_args, **options)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/ak-root/venv/lib/python3.11/site-packages/celery/canvas.py", line 1598, in apply_async
    results = list(self._apply_tasks(tasks, producer, app, p,
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/ak-root/venv/lib/python3.11/site-packages/celery/canvas.py", line 1775, in _apply_tasks
    sig.apply_async(producer=producer, add_to_parent=False,
  File "/ak-root/venv/lib/python3.11/site-packages/celery/canvas.py", line 400, in apply_async
    return _apply(args, kwargs, **options)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/ak-root/venv/lib/python3.11/site-packages/celery/app/task.py", line 594, in apply_async
    return app.send_task(
           ^^^^^^^^^^^^^^
  File "/ak-root/venv/lib/python3.11/site-packages/celery/app/base.py", line 798, in send_task
    amqp.send_task_message(P, name, message, **options)
  File "/ak-root/venv/lib/python3.11/site-packages/celery/app/amqp.py", line 517, in send_task_message
    ret = producer.publish(
          ^^^^^^^^^^^^^^^^^
  File "/ak-root/venv/lib/python3.11/site-packages/kombu/messaging.py", line 175, in publish
    body, content_type, content_encoding = self._prepare(
                                           ^^^^^^^^^^^^^^
  File "/ak-root/venv/lib/python3.11/site-packages/kombu/messaging.py", line 268, in _prepare
    body) = dumps(body, serializer=serializer)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/ak-root/venv/lib/python3.11/site-packages/kombu/serialization.py", line 219, in dumps
    with _reraise_errors(EncodeError):
  File "/usr/local/lib/python3.11/contextlib.py", line 155, in __exit__
    self.gen.throw(typ, value, traceback)
  File "/ak-root/venv/lib/python3.11/site-packages/kombu/serialization.py", line 45, in _reraise_errors
    reraise(wrapper, wrapper(exc), sys.exc_info()[2])
  File "/ak-root/venv/lib/python3.11/site-packages/kombu/exceptions.py", line 34, in reraise
    raise value.with_traceback(tb)
  File "/ak-root/venv/lib/python3.11/site-packages/kombu/serialization.py", line 41, in _reraise_errors
    yield
  File "/ak-root/venv/lib/python3.11/site-packages/kombu/serialization.py", line 220, in dumps
    payload = encoder(data)
              ^^^^^^^^^^^^^
  File "/ak-root/venv/lib/python3.11/site-packages/kombu/utils/json.py", line 57, in dumps
    return _dumps(s, cls=cls, **dict(default_kwargs, **kwargs))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
          ^^^^^^^^^^^
  File "/usr/local/lib/python3.11/json/encoder.py", line 200, in encode
    chunks = self.iterencode(o, _one_shot=True)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/json/encoder.py", line 258, in iterencode
    return _iterencode(o, 0)
           ^^^^^^^^^^^^^^^^^
  File "/ak-root/venv/lib/python3.11/site-packages/kombu/utils/json.py", line 45, in default
    return super().default(o)
           ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/json/encoder.py", line 180, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
kombu.exceptions.EncodeError: Object of type MIMEImage is not JSON serializable
```

**Version and Deployment (please complete the following information):**

-   authentik version: 2023.10.1
-   Deployment: docker-compose



Discussion:

Resolving patch:
diff --git a/authentik/stages/email/tasks.py b/authentik/stages/email/tasks.py
--- a/authentik/stages/email/tasks.py
+++ b/authentik/stages/email/tasks.py
@@ -13,6 +13,7 @@
 from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
 from authentik.root.celery import CELERY_APP
 from authentik.stages.email.models import EmailStage
+from authentik.stages.email.utils import logo_data
 
 LOGGER = get_logger()
 
@@ -81,6 +82,10 @@ def send_mail(self: MonitoredTask, message: dict[Any, Any], email_stage_pk: Opti
         # Because we use the Message-ID as UID for the task, manually assign it
         message_object.extra_headers["Message-ID"] = message_id
 
+        # Add the logo (we can't add it in the previous message since MIMEImage
+        # can't be converted to json)
+        message_object.attach(logo_data())
+
         LOGGER.debug("Sending mail", to=message_object.to)
         backend.send_messages([message_object])
         Event.new(
diff --git a/authentik/stages/email/utils.py b/authentik/stages/email/utils.py
--- a/authentik/stages/email/utils.py
+++ b/authentik/stages/email/utils.py
@@ -9,7 +9,7 @@
 
 
 @lru_cache()
-def logo_data():
+def logo_data() -> MIMEImage:
     """Get logo as MIME Image for emails"""
     path = Path("web/icons/icon_left_brand.png")
     if not path.exists():
@@ -29,5 +29,4 @@ def __init__(self, template_name=None, template_context=None, language="", **kwa
         super().__init__(**kwargs)
         self.content_subtype = "html"
         self.mixed_subtype = "related"
-        self.attach(logo_data())
         self.attach_alternative(html_content, "text/html")


Test patch: