plain-loginlink changelog
0.22.0 (2026-09-04)
What's changed
- Link expiration is now a
link_expires_inclass attribute onLoginLinkForminstead of anexpires_inargument tomaybe_send_link(). Overriding a method just to change a number meant asuper()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.signingfor building a genuinely single-use flow (927bcd11ed)
Upgrade instructions
If you overrode
maybe_send_link()only to changeexpires_in, replace it with the class attribute:class CustomLoginLinkForm(LoginLinkForm): link_expires_in = 60 * 15 # 15 minutesIf you call
maybe_send_link()directly, drop theexpires_inargument — it no longer accepts one.
0.21.9 (2026-08-12)
What's changed
urlsdeclared 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=302toRedirectResponse, 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
nextquery param is honored, but an empty or externalnextfalls 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
plain0.145.0. Canonical URLs now follow the project'sURLS_TRAILING_SLASHsetting. (48ca69bafa)
Upgrade instructions
- No changes required.
0.21.4 (2026-05-12)
What's changed
FormViewandTemplateViewimports moved fromplain.viewstoplain.templates.viewsto match theplain.templatescarve-out. The end-userLoginLinkViewAPI is unchanged. (19b622a7ca)- Pins
plain>=0.143.0,plain.email>=0.19.3, andplain.templates>=0.1.0.
Upgrade instructions
- No code changes required if you upgrade
plainto 0.143.0 in the same step.
0.21.3 (2026-05-05)
What's changed
- Exposes
__version__fromimportlib.metadataonplain.loginlinkfor 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.Userconvention. (0861c9915cb6) - Updated login form view to use the generic
FormView[FormClass]. (8dbe9e413d30) - Migrated type suppression comments to
ty: ignorefor the new ty checker version. (4ec631a7ef51)
Upgrade instructions
- No changes required — but see the
plain0.132.0 notes if you haven't moved your User model toapp/users/models.py.
0.21.1 (2026-03-27)
What's changed
- Replaced
signing.dumps/signing.loadswrappers with directExpiringSignerusage for login link tokens (99b0e57bc175) - Updated form fields from
CharFieldtoTextField(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.modelstoplain.postgresin links.py.
Upgrade instructions
- Update imports:
from plain.modelstofrom plain.postgres,from plain import modelstofrom plain import postgres.
0.20.1 (2026-03-11)
What's changed
- Simplified redirect logic in
LoginLinkSentView— removed intermediate variable fornextquery 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 passtokenas a keyword argument toreverse()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
RedirectResponseclass (wasResponseRedirect) (fad5bf2)
Upgrade instructions
- No changes required
0.16.0 (2025-12-04)
What's changed
- Internal refactoring of
ExpiringSignerto 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
AuthViewinstead of usingAuthViewMixinfor improved type checking support (569afd6)
Upgrade instructions
- No changes required
0.14.0 (2025-11-13)
What's changed
- The
expires_inparameter indumps()andsign_object()is now keyword-only and required (f4dbcef) - The
keyparameter inloads()is now keyword-only (f4dbcef)
Upgrade instructions
- Update any direct calls to
dumps()to passexpires_inas a keyword argument (e.g.,dumps(obj, expires_in=3600)instead ofdumps(obj, 3600)) - Update any direct calls to
loads()to passkeyas a keyword argument if specified (e.g.,loads(s, key="mykey")instead ofloads(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
AuthViewMixinto 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
objectstoqueryfor 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.objectswithUser.queryin your loginlink-related code
0.11.0 (2025-07-22)
What's changed
- Login link generation now uses
user.idinstead ofuser.pkfor 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.