v0.163.0

plain-loginlink changelog

0.22.0 (2026-09-04)

What's changed

  • Link expiration is now a link_expires_in class attribute on LoginLinkForm instead of an expires_in argument to maybe_send_link(). Overriding a method just to change a number meant a super() call whose only job was passing one value through; the attribute says the same thing where you'd look for it (927bcd11ed)
  • The README now documents that login links are not single-use — a link works every time it is clicked until it expires, and the expiration window is the security boundary. Consuming a link on first request breaks with corporate mail scanners (Safe Links, Mimecast, Barracuda), which fetch URLs in incoming email and would spend the link before the recipient ever clicks it. The new FAQ entry explains the tradeoff and points at plain.signing for building a genuinely single-use flow (927bcd11ed)

Upgrade instructions

  • If you overrode maybe_send_link() only to change expires_in, replace it with the class attribute:

    class CustomLoginLinkForm(LoginLinkForm):
        link_expires_in = 60 * 15  # 15 minutes
    
  • If you call maybe_send_link() directly, drop the expires_in argument — it no longer accepts one.

0.21.9 (2026-08-12)

What's changed

  • urls declared as a tuple; internal lint cleanups (f52e18f532)

Upgrade instructions

  • No changes required.

0.21.8 (2026-08-02)

What's changed

  • Login-link redirects now pass an explicit status_code=302 to RedirectResponse, per the new requirement in plain 0.155.0. Behavior is unchanged. (caa718b4bf)

Upgrade instructions

  • No changes required.

0.21.7 (2026-07-15)

What's changed

  • Already-logged-in users hitting the login pages are now redirected safely. The next query param is honored, but an empty or external next falls back to the default target instead of raising, and redirect handling across the login views is unified through one helper. (1486c685ed)

Upgrade instructions

  • No changes required.

0.21.6 (2026-06-03)

What's changed

  • Added a test-only dev dependency group (plain.pytest, plain.auth) for new behavior regression tests. No runtime changes. (891a0c1c41)

Upgrade instructions

  • No changes required.

0.21.5 (2026-05-13)

What's changed

  • Updated route definitions to the no-slash convention introduced in plain 0.145.0. Canonical URLs now follow the project's URLS_TRAILING_SLASH setting. (48ca69bafa)

Upgrade instructions

  • No changes required.

0.21.4 (2026-05-12)

What's changed

  • FormView and TemplateView imports moved from plain.views to plain.templates.views to match the plain.templates carve-out. The end-user LoginLinkView API is unchanged. (19b622a7ca)
  • Pins plain>=0.143.0, plain.email>=0.19.3, and plain.templates>=0.1.0.

Upgrade instructions

  • No code changes required if you upgrade plain to 0.143.0 in the same step.

0.21.3 (2026-05-05)

What's changed

  • Exposes __version__ from importlib.metadata on plain.loginlink for version probes that don't want to scrape pip metadata. (c6cf6edb)

Upgrade instructions

  • No changes required.

0.21.2 (2026-04-13)

What's changed

  • Updated internal references to use the fixed app.users.models.User convention. (0861c9915cb6)
  • Updated login form view to use the generic FormView[FormClass]. (8dbe9e413d30)
  • Migrated type suppression comments to ty: ignore for the new ty checker version. (4ec631a7ef51)

Upgrade instructions

  • No changes required — but see the plain 0.132.0 notes if you haven't moved your User model to app/users/models.py.

0.21.1 (2026-03-27)

What's changed

  • Replaced signing.dumps/signing.loads wrappers with direct ExpiringSigner usage for login link tokens (99b0e57bc175)
  • Updated form fields from CharField to TextField (4e29f5d6cade)

Upgrade instructions

  • Requires plain>=0.129.0. No other changes required.

0.21.0 (2026-03-12)

What's changed

  • Updated type import from plain.models to plain.postgres in links.py.

Upgrade instructions

  • Update imports: from plain.models to from plain.postgres, from plain import models to from plain import postgres.

0.20.1 (2026-03-11)

What's changed

  • Simplified redirect logic in LoginLinkSentView — removed intermediate variable for next query param (5edfb2bedf90)

Upgrade instructions

  • Requires plain>=0.123.0. No other changes required.

0.20.0 (2026-03-07)

What's changed

  • Updated generate_link_url() to pass token as a keyword argument to reverse() instead of a positional argument (6eecc35)

Upgrade instructions

  • No changes required.

0.19.0 (2026-03-06)

What's changed

  • Updated URL patterns to pass view classes directly instead of calling .as_view(), adapting to the view API changes in plain 0.118.0 (0d0c8a64cb45)

Upgrade instructions

  • Requires plain>=0.118.0.

0.18.1 (2026-02-26)

What's changed

  • Auto-formatted config files with updated linter configuration (028bb95c3ae3)

Upgrade instructions

  • No changes required.

0.18.0 (2026-01-13)

What's changed

  • Expanded README with comprehensive documentation including usage examples, customization options, and FAQs (da37a78)

Upgrade instructions

  • No changes required

0.17.0 (2026-01-13)

What's changed

  • Internal update to use renamed RedirectResponse class (was ResponseRedirect) (fad5bf2)

Upgrade instructions

  • No changes required

0.16.0 (2025-12-04)

What's changed

  • Internal refactoring of ExpiringSigner to use composition instead of inheritance for better type safety (ac1eeb0)

Upgrade instructions

  • No changes required

0.15.0 (2025-11-24)

What's changed

  • Views now inherit from AuthView instead of using AuthViewMixin for improved type checking support (569afd6)

Upgrade instructions

  • No changes required

0.14.0 (2025-11-13)

What's changed

  • The expires_in parameter in dumps() and sign_object() is now keyword-only and required (f4dbcef)
  • The key parameter in loads() is now keyword-only (f4dbcef)

Upgrade instructions

  • Update any direct calls to dumps() to pass expires_in as a keyword argument (e.g., dumps(obj, expires_in=3600) instead of dumps(obj, 3600))
  • Update any direct calls to loads() to pass key as a keyword argument if specified (e.g., loads(s, key="mykey") instead of loads(s, "mykey"))

0.13.2 (2025-10-31)

What's changed

  • Added BSD-3-Clause license to package metadata (8477355)

Upgrade instructions

  • No changes required

0.13.1 (2025-10-06)

What's changed

  • Added comprehensive type annotations to improve IDE and type checker support (634489d)

Upgrade instructions

  • No changes required

0.13.0 (2025-10-02)

What's changed

  • Login views now use AuthViewMixin to access user and session data instead of request attributes (154ee10)

Upgrade instructions

  • No changes required

0.12.0 (2025-09-12)

What's changed

  • Model managers renamed from objects to query for consistency with Plain framework (037a239)
  • Minimum Python version raised from 3.11 to 3.13 (d86e307)
  • README updated with proper formatting and installation instructions (4ebecd1)

Upgrade instructions

  • Replace any custom usage of User.objects with User.query in your loginlink-related code

0.11.0 (2025-07-22)

What's changed

  • Login link generation now uses user.id instead of user.pk for consistency (4b8fa6a)

Upgrade instructions

  • No changes required.

0.10.1 (2025-06-23)

What's changed

  • No user-facing changes in this release.

Upgrade instructions

  • No changes required.