# django-sesame: token URLs that log a Django user in

> django-sesame is a Django authentication backend that issues URLs containing a one-time token, so a user can be logged in by opening a link. It suits email login and authenticated report links, but a token in a URL is a bearer secret, and the project says so plainly.

**aaugustin/django-sesame** — "Magic Links" - URLs with authentication tokens for one-click login

- Repository: https://github.com/aaugustin/django-sesame
- Website: https://django-sesame.readthedocs.org/
- Stars: 1,033 · Forks: 56
- Language: Python
- License: BSD-3-Clause
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/aaugustin-django-sesame

## A URL that carries the credential with it

django-sesame removes one step from a Django sign-in flow: typing a password. It generates URLs that embed an authentication token, for example a query string of the form ?sesame=zxST9d0XT9xgfYLvoa9e2myN, and the project authenticates the user from the token it finds there. The project describes itself as frictionless authentication with "Magic Links", and the README positions it for a set of concrete situations: logging in by email, sending a link to a report that is ready, sharing a link with a guest, authenticating a WebSocket connection, and running a small private site where nobody wants to manage a password.

That last case is named as the original reason the project exists, and it is the honest framing to keep in mind. This is a tool for reducing friction on a link that will travel through email, a chat client, or a chat message, and that travel is the whole point of the design and also its main weakness.

## The backend plugs into AUTHENTICATION_BACKENDS, not into a custom login view

The mechanism is deliberately small. django-sesame ships an authentication backend named sesame.backends.ModelBackend. You add it to the AUTHENTICATION_BACKENDS setting, and the project then authenticates users based on the tokens it finds in URLs. It requires django.contrib.auth, and the package metadata pins django >= 5.2 and python >= 3.11, with an optional ua-parser dependency exposed through an extra named ua.

Two helpers make up the visible surface. sesame.utils.get_query_string(user) returns the query string that identifies a user, and sesame.views.LoginView is a class based view that consumes it. A login view is wired into the URLconf in the usual Django way, and the token then travels in the query string, which is what makes the pattern work over plain email links. The README also states that tokens are secured with modern cryptography and that there are configurable options for token invalidation. It does not document the internals of that cryptography on its front page, so anyone who needs to reason about the token format itself has to read the documentation on ReadTheDocs.

## Installing the package and wiring up a first token login

Installation is a single PyPI package, and the only setup step is registering the backend. The README gives this as the install command:

```bash
pip install django-sesame
```

Then the project's settings gain a second entry in AUTHENTICATION_BACKENDS, extending the default value rather than replacing it:

```python
AUTHENTICATION_BACKENDS = [
    "django.contrib.auth.backends.ModelBackend",
    "sesame.backends.ModelBackend",
]
```

With the backend registered, the project can authenticate users based on django-sesame tokens. A URLconf entry gives the token somewhere to land:

```python
from django.urls import path
from sesame.views import LoginView

urlpatterns = [
    ...,
    path("sesame/login/", LoginView.as_view(), name="sesame-login"),
    ...,
]
```

The README then generates a login URL for an existing user, pulling one from the database with get_user_model() and appending get_query_string(user):

```python
>>> from sesame.utils import get_query_string
>>> LOGIN_URL = "https://127.0.0.1:8000/sesame/login/"
>>> LOGIN_URL + get_query_string(user)
'https://127.0.0.1:8000/sesame/login/?sesame=zxST9d0XT9xgfYLvoa9e2myN'
```

The README notes that your token will differ from that example. The verification step is simple: log out, open the generated URL, and the browser is logged in. The project also ships a Makefile whose test target runs the suite with the Django test settings, which is how a fork would run the same checks.

## SESAME_MAX_AGE is the setting that decides how long a link stays usable

Because the credential is a URL, its lifetime is a design choice rather than a side effect, and django-sesame exposes that choice as SESAME_MAX_AGE. The README gives two concrete calibrations. For login by email, which it calls an attractive option on mobile where typing passwords is uncomfortable, it suggests a small SESAME_MAX_AGE, perhaps 10 minutes. For authenticated links, where a report is generated offline and emailed when ready, it suggests an appropriate SESAME_MAX_AGE, probably a few days.

The gap between ten minutes and a few days is the interesting part. The same setting covers two flows with very different exposure: a short lived login link that is read on a phone, and a long lived report link that sits in a mailbox for a week. If you set one global value for both, the email login window is too generous or the report link expires too early. The README does not describe a per use case expiry mechanism, so the operational answer is to pick the value that matches the riskier flow and handle the other one separately.

## Whoever holds the token holds the session, and the README says so

The security section is unusually direct. The major weakness in django-sesame is a direct consequence of the feature it implements: whoever obtains an authentication token is able to authenticate to the website. The README lists where URLs end up: emails, referer headers, proxy logs, browser history. It does not try to argue that this can be avoided.

The mitigation offered is lifetime and reusability, creating short lived or single use tokens. Beyond that, the project claims a reasonable attempt was made to provide a secure solution, with modern cryptography for tokens and configurable options for token invalidation. The valid reading of that combination is that django-sesame gives you the levers to shrink the exposure window, not a way to remove the exposure itself. Anyone evaluating the package should plan for token lifetime and invalidation to be the two settings that actually matter, and should treat the link as a password in transit, because that is what it is.

## Sharing and reporting flows should authenticate without logging the user in

The most useful guidance in the README is a warning attached to two of the use cases. Authenticated links and sharing links are likely to be forwarded, and a forwarded email goes to a mailbox you do not control. In both cases the README says the link should not log the user in, and should only allow access to specific views.

This is a difference in mechanism rather than in wording, and it is where the project is more opinionated than its simplicity suggests. A magic link flow and an authenticated report link look identical on the wire, one token in a query string. The distinction is whether the view behind the token establishes a session or merely authorizes a single request. The project ships the authentication backend that does the former, and it leaves the latter to the host application, which is also why the README frames authenticated links as a use case you build rather than one you switch on.

The same logic applies to sharing. The README says you may create a phantom account for the guest and generate a link tied to that account, or reuse the user's account. Creating that account is the host project's job, since the README does not describe account creation as a feature of django-sesame.

## The wrong tool for accounts that need a password, a second factor, or revocation

django-sesame is the wrong tool in a specific and common situation: when identity has to survive a leaked link. A password can be changed after it leaks, and a session can be revoked on the server. A URL token has no password to rotate, so invalidation rests on the token options you configure, which is why the README's list of use cases is full of links that are consumed once, forwarded at most, and worth little to a stranger. Asking the package to protect a bank balance, a payroll export, or an administrator console means asking it to do the one thing it explicitly cannot, and no amount of SESAME_MAX_AGE tuning changes that.

It is also the wrong tool when the reader is not a Django user. The package is a Django authentication backend and requires django.contrib.auth, so it plugs into a Django project's auth machinery and does not offer a standalone token service for another stack. And for a low stakes private site, the default settings are enough: the README calls the small family or club site the original, admittedly niche, use case for which django-sesame was built, where authorized users can bookmark personalized authenticated URLs.

## BSD-3-Clause, version 3.2.5, and an upgrade path tied to Django releases

The licensing position is simple. The package metadata in pyproject.toml declares license = "BSD-3-Clause", and the repository carries a LICENSE file, so the code is usable in closed and open projects under the usual three clause terms, without the copyleft obligations of a GPL style licence. Anyone with a compliance process should still read the LICENSE file itself rather than rely on a metadata field.

The version surface is less settled. The project version in pyproject.toml is 3.2.5, and the repository has no GitHub releases, so there is no published changelog to read in the repository. The README states the tested matrix instead: Django 5.2 (LTS), 6.0, and 6.1, on Python 3.11 or later. The package metadata lists Development Status :: 6 - Mature, and the last push to the repository was on 2026-10-02, so the code is not sitting still.

That Django matrix is the real upgrade cost. Because the supported Django versions move with the Django release cycle, a project on a Django version outside the tested set is effectively running an untested combination, and the constraint django >= 5.2 also rules out the older Django releases many existing applications still run. The upgrade path for a consumer is therefore a Django upgrade, and the test suite in the repository, runnable through the Makefile test target, is the quickest way to see whether a pairing behaves as expected.

## Conclusion

django-sesame is a reasonable fit for a Django site that wants email-based login, authenticated report links, or WebSocket token checks, and it is a poor fit wherever a leaked URL must not grant access, since the token itself is the credential. Before adopting it, confirm that SESAME_MAX_AGE and the token invalidation options are set to a lifetime matching the use case, check that the email and sharing flows do not call the login view, and verify that the Django version in production is one of 5.2, 6.0 or 6.1, because the package requires django >= 5.2 and python >= 3.11.

## FAQ

### How do I add magic link login to a Django project with django-sesame?

Install the package with pip install django-sesame, then add "sesame.backends.ModelBackend" to the AUTHENTICATION_BACKENDS setting alongside the default django.contrib.auth.backends.ModelBackend, and route a path such as "sesame/login/" to sesame.views.LoginView. The README then uses sesame.utils.get_query_string(user) to build a login URL for an existing user.

### How long should a django-sesame token stay valid?

The README suggests a small SESAME_MAX_AGE, perhaps 10 minutes, for login by email, and a larger value, probably a few days, for authenticated links to a finished report. The shorter value limits the damage from a link that sits in an inbox.

### Is a django-sesame magic link safe to email to someone?

The README says whoever obtains an authentication token is able to authenticate to the website, and that URLs end up in emails, referer headers, proxy logs and browser history. For authenticated links and sharing links the README advises that they should not log the user in and should only allow access to specific views.

### Does django-sesame replace Django's normal password login?

No. The AUTHENTICATION_BACKENDS example extends the default value rather than replacing it, so django.contrib.auth.backends.ModelBackend and sesame.backends.ModelBackend are listed together. The package requires django.contrib.auth and adds a token based path alongside the standard one.

### What Django and Python versions does django-sesame support?

The README states it is tested with Django 5.2 (LTS), 6.0, and 6.1, and Python 3.11 or later. The package metadata declares django = ">=5.2" and python = ">=3.11" as runtime dependencies.

## Sources

- [aaugustin/django-sesame on GitHub](https://github.com/aaugustin/django-sesame)
- [Issues](https://github.com/aaugustin/django-sesame/issues)
- [License: BSD-3-Clause](https://github.com/aaugustin/django-sesame/blob/main/LICENSE)
- [Project website](https://django-sesame.readthedocs.org/)
- [README](https://github.com/aaugustin/django-sesame/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/aaugustin-django-sesame
