# django-msgraphbackend

**URL:** <https://forum.djangoproject.com/t/django-msgraphbackend/34933>\
**Category:** Show & Tell\
**Created:** [September 20, 2024, 8:53am UTC](https://forum.djangoproject.com/t/django-msgraphbackend/34933 "2024-09-20T08:53:20Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![doom](https://avatars.discourse-cdn.com/v4/letter/d/a587f6/32.png) [@doom](https://forum.djangoproject.com/u/doom)\
**Post date:** [September 20, 2024, 8:53am UTC](https://forum.djangoproject.com/t/django-msgraphbackend/34933/1 "2024-09-20T08:53:20Z")

</div>

I have written an email backend that sends via the Microsoft Graph API. What sets this apart from other apps is, that the app does not require any dependencies and allows multiple different configuration types from most permissive and simple to deploy – to most restrictive and secure, which none of the other apps allow you to do.

The app is available via pip (pip install django-msgraphbackend) and the readme/docs can be found in the GitHub Repository ([GitHub - danielniccoli/django-msgraphbackend: Microsoft Graph Backend for Django – An dependency-free email backend for Django that sends emails via Microsoft Graph.](https://github.com/danielniccoli/django-msgraphbackend))

---

<div class="post-metadata">

**Author:** ![doom](https://avatars.discourse-cdn.com/v4/letter/d/a587f6/32.png) [@doom](https://forum.djangoproject.com/u/doom)\
**Post date:** [August 4, 2026, 10:31pm UTC](https://forum.djangoproject.com/t/django-msgraphbackend/34933/2 "2026-08-04T22:31:23Z")

</div>

## v5.2.0 is released

### Breaking changes

- Dropped support for Django 4.2, 5.0, and 5.1, and Python 3.9. Now requires **Django ≥5.2** and **Python ≥3.10**.

### Security

- Fixed an OData filter injection issue in `_get_user()` — the `from_address` value is now properly escaped before being used in the Microsoft Graph `$filter` query.

### Fixed

- `fail_silently=True` is now respected consistently across token acquisition, sending, and user lookup. Previously, some failure paths could still raise anyway — or, in one case, crash with an unrelated `NameError` instead of failing silently.
- Error messages now surface Microsoft Graph’s actual response body instead of assuming a fixed `{"error": {"code", "message"}}` shape that didn’t always hold.

### Improved

- Failures are now logged via the standard `logging` module when `fail_silently=True`, so silent failures are still observable.
- Clarified `MSGraphToken`’s internal field naming — the raw `expires_in`/`ext_expires_in` values from the API response are now kept separate from the computed `expires_at`/`ext_expires_at` deadlines.
- Added type hints throughout and enabled static type checking (pyrefly).

### CI / release process

- Fixed PyPI publishing running in parallel with, instead of after, TestPyPI publishing.
- Release builds now fail if the pushed tag doesn’t match the version in `pyproject.toml`.

---

<div class="post-metadata">

**Author:** ![Louis2608](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/louis2608/32/33307_2.png) [@Louis2608](https://forum.djangoproject.com/u/Louis2608)\
**Post date:** [September 2, 2026, 7:08am UTC](https://forum.djangoproject.com/t/django-msgraphbackend/34933/3 "2026-09-02T07:08:46Z")

</div>

dropping Python 3.9 and Django 4.2 LTS support in one hit is definitely going to surprise a few people who aren’t reading release notes, but requiring Python 3.10+ makes sense long term.

Super glad to see these specific fixes in this release:

OData Injection Patch: Properly escaping from\_address in \_get\_user() is a huge security fix if you’re dealing with Graph queries.

Consistent fail\_silently: No more random NameError crashes or unhandled exceptions when silence is explicitly requested, plus logging silent failures makes debugging way less painful.

Actual Graph Error Bodies: Surfacing the real payload instead of assuming the hardcoded error schema will save so much headache when MS Graph acts up.

Solid release overall, just double-check your Python/Django versions before upgrading!

---

<div class="post-metadata">

**Author:** ![doom](https://avatars.discourse-cdn.com/v4/letter/d/a587f6/32.png) [@doom](https://forum.djangoproject.com/u/doom)\
**Post date:** [September 2, 2026, 7:54am UTC](https://forum.djangoproject.com/t/django-msgraphbackend/34933/4 "2026-09-02T07:54:45Z")

</div>

Thanks for the feedback @Louis2608 !

Since 4.2 LTS is no longer supported, and Django 5.2 as the oldest supported version requires Python 3.10, it made sense to drop Python 3.9 with Django 4.2 LTS. Hence, the bigger change.

The pyproject.toml requires both, Django 5.2 and Python 3.10 as a minimum, so hopefully no one will run into surprises as the new version should not be installed unless forced.
