.. :changelog: Changelog ========= Unreleased ---------- * Removed Beads and Beadsflow project-tracking files and instructions * Added ``BACKLOG.md`` and ``DONE.md`` as the repository-local work tracking workflow * Linked the backlog workflow from the project documentation * Clarified that completed backlog items must keep docs and changelog entries aligned with implementation changes * Switched hook execution from ``pre-commit`` to ``prek`` and updated hook revisions for Python 3.14 compatibility * Added a dedicated backlog page to the documentation navigation * Raised dependency floors and refreshed ``uv.lock`` to resolve open Dependabot alerts for Django, pytest, requests, urllib3, sqlparse, Pygments, filelock, and virtualenv * Added ``Token.expires_at`` plus the ``INDIEWEB_TOKEN_EXPIRES_IN`` setting (default 86400 seconds); ``TokenView`` now persists the expiration and reports the live remaining lifetime in ``expires_in`` * ``TokenAuthMixin`` now rejects expired access tokens with HTTP 401; tokens issued before this change have ``expires_at=NULL`` and remain valid until reissued * Fixed the IndieAuth auth-code timeout calculation to use ``timedelta.total_seconds()`` so codes older than one day are now correctly rejected * Validated IndieAuth ``redirect_uri`` values: the authorization endpoint rejects malformed values (invalid URL, any ``#`` delimiter, userinfo, or a non-``http``/``https`` scheme) with HTTP 400 before issuing a code, and the token endpoint rejects malformed submissions with ``invalid_grant``; the comparison between the submitted and stored values now lowercases scheme and host while preserving path and query verbatim * Fixed the consent approval and denial redirects to merge ``code``/``state``/``me`` (or ``error``/``state``) into an existing ``redirect_uri`` query string instead of breaking it with a duplicate ``?`` separator * Added PKCE (RFC 7636) support to the IndieAuth authorization and token endpoints: the authorization endpoint accepts ``code_challenge`` and ``code_challenge_method`` (``S256`` and ``plain``, defaulting to ``plain`` when only the challenge is sent) and stores them on the ``Auth`` row; the token endpoint requires a matching ``code_verifier`` whenever a challenge was stored, comparing in constant time. Auth codes issued before this change continue to be redeemable without a ``code_verifier``, preserving backwards compatibility for legacy clients. Migration ``0011_auth_code_challenge`` adds the new nullable ``Auth.code_challenge`` and ``Auth.code_challenge_method`` columns. * Added ``client_id`` access control. The authorization endpoint (GET, consent POST, code-verification POST) and the token endpoint now reject malformed ``client_id`` values (invalid URL, any ``#`` delimiter, userinfo, or a non-``http``/``https`` scheme) with HTTP 400 *before* creating any state. A new optional ``INDIEWEB_CLIENT_ID_VALIDATOR`` setting (dotted path to a ``(client_id: str) -> bool`` callable) runs on top of structural validation at all four authorization/token paths and again on the Micropub resource-server path, so revoking a client takes effect immediately for previously-issued tokens. A misconfigured validator fails closed: the authorization paths return plain-text HTTP 400 (``invalid client_id`` for structural failures, ``invalid_client`` for validator failures), the token endpoint returns HTTP 400 ``invalid_request`` with content type ``application/x-www-form-urlencoded``, and the Micropub resource server returns HTTP 403 ``invalid_client``. Stored ``client_id`` values are not re-validated structurally on use (matching the ``redirect_uri`` rule); the configured validator IS re-applied on use. Default behavior (setting unset) is unchanged: every structurally-valid ``client_id`` is permitted. * Enforced per-operation scopes on the Micropub endpoint. ``POST`` entry create now requires ``create`` (the legacy alias ``post`` is still accepted); ``POST action=update`` requires ``update``; ``POST action=delete`` requires ``delete``; ``POST action=undelete`` requires ``undelete``; ``GET ?q=source`` requires ``update``. ``GET ?q=config``, ``GET ?q=syndicate-to``, and ``GET`` with no ``q`` only require an authenticated token (no scope gate). Stored ``scope`` is split on whitespace and compared as an exact token, so values like ``createXYZ`` no longer satisfy ``create`` (previously a substring match). Scope failures still return HTTP 403 with the plain-text body ``authorization error``. Closes the previously over-permissive scope check that allowed a ``create``-only token to reach update/delete/undelete code paths. * Normalized IndieAuth scope strings before consent display and auth-code storage by splitting on whitespace, removing duplicate tokens while preserving first-seen order, and joining with single spaces. Unknown scopes are intentionally preserved. The token endpoint now issues the stored auth-code scope and rejects any submitted ``scope`` that normalizes differently with HTTP 400 ``invalid_grant`` and content type ``application/x-www-form-urlencoded``, without creating or reissuing a token. * Implemented the Micropub source query on ``GET /indieweb/micropub/?q=source&url=...``. The query now uses the configured ``MicropubContentHandler.get_entry(url, user)`` method after the existing ``update`` scope check, returns full source content as ``{"type": [...], "properties": {...}}``, and supports the W3C ``properties[]=NAME`` filter form by returning only existing requested properties as ``{"properties": {...}}``. Missing or unknown ``url`` values return ``400 invalid_request``; unexpected handler exceptions return ``500`` and are logged via ``logger.exception``. This completes the source-query gap left by the partial editing-support slice. * Implemented the Micropub ``update``, ``delete``, and ``undelete`` actions on ``POST /indieweb/micropub/`` by dispatching into the configured ``MicropubContentHandler``. ``POST action=update`` is JSON-only (per Micropub §3.7); the body must include at least one of ``replace``, ``add``, or ``delete`` and values inside each operation must be arrays per §3.4 — empty bodies, scalar operation values, and non-conformant ``delete`` shapes are rejected with ``400 invalid_request`` rather than papered over by the handler. ``POST action=delete`` and ``POST action=undelete`` accept either form-encoded or JSON bodies, both with a required ``url``. Successful updates and undeletes return ``204 No Content``, or ``201 Created`` with a ``Location`` header when the configured handler relocates the entry; successful deletes return ``204 No Content`` (delete cannot relocate because the handler interface returns ``None`` on delete). Unknown URLs (the handler raises ``ValueError``), missing ``url``, malformed JSON, non-object JSON bodies, and form-encoded ``action=update`` requests return ``400 invalid_request``; unexpected handler exceptions return ``500`` (logged via ``logger.exception``). Per-operation scope enforcement and the existing ``403 authorization error`` and ``403 invalid_client`` shapes are unchanged. * Hardened the Micropub endpoint against malformed JSON bodies. A ``POST`` with ``Content-Type: application/json`` whose body fails to parse (including invalid UTF-8 bytes such as ``b"\xff"`` that surface as ``UnicodeDecodeError`` rather than ``json.JSONDecodeError``), or whose body parses to anything other than a JSON object, is now rejected with ``400 invalid_request`` *before* scope or action dispatch. Previously such requests fell through to the create path and could silently create an empty entry when the token had ``create`` scope, or escape as a ``500`` for invalid UTF-8. * Webmention receive-side target verification now parses source-page ``href`` attributes and compares common canonical URL variants instead of relying on raw ``href`` string matching. Fragments are ignored; scheme/host case, a leading ``www.``, one non-root trailing slash, and query-parameter ordering are normalized for matching. Microformats2 target matching uses the same policy for reply/like/repost classification while stored ``Webmention.source_url`` and ``Webmention.target_url`` remain the submitted values. * Webmention receive-side reprocessing now clears stale verification timestamps when a source can no longer be verified. Existing Webmentions whose source returns ``410 Gone`` or whose HTML no longer links to the submitted target are marked ``failed`` with ``verified_at=NULL`` while preserving submitted URLs and previously parsed author/content fields. Other processor failure paths also clear ``verified_at`` when they mark a row ``failed``, and spam reclassification clears ``verified_at`` when a row is marked ``spam``. A later valid source can be reprocessed back to ``verified`` with a fresh timestamp. * Webmention receive and send HTTP requests now follow redirects explicitly with a limit of 5 redirects and only continue to ``http``/``https`` URLs. Receive-side source fetches parse the final response URL as the microformats2 base while preserving submitted source/target URLs; send-side endpoint discovery resolves relative endpoints against the final target page URL; source-content fetches follow the same policy; endpoint ``POST`` delivery preserves the Webmention form payload across followed redirects. * Webmention receive-side author extraction now completes the local/same-page authorship fallback chain. Explicit ``h-entry`` author data remains highest priority; URL-valued authors still resolve to matching same-page ``h-card`` items or fall back to URL-as-name when unmatched. Entries without explicit authors can now use ``rel=author`` links that point to an ``h-card`` already present in the fetched document, including same-page fragment references, and then a single unambiguous page-level ``h-card`` fallback. Relative author and photo URLs resolve against the final fetched source URL after redirects. Remote author-page fetching remains unsupported. * Webmention receive-side author ``h-card`` lookup now uses the same conservative canonical URL matching policy already used for target verification. Explicit URL-valued ``p-author`` references and local ``rel=author`` links can now match same-page ``h-card`` ``u-url`` values across supported variants such as scheme/host case, a leading ``www.``, one non-root trailing slash, ignored fragments, and query-parameter ordering. Unmatched explicit author URLs still fall back to URL-as-name, and no remote author fetching was added. 0.5.3 (2025-10-28) ------------------ * Fixed webmention author extraction when author is referenced as a URL string * Implemented partial microformats2 authorship algorithm to resolve author URLs to h-cards on the same page * Added recursive search for h-cards and h-entries in nested structures (e.g., h-feeds) * Fixed regression where missing h-cards would result in empty author names * Author URL now used as fallback name when matching h-card cannot be found * Previously displayed the author URL as the name when parsing feed.city-style webmentions * Now correctly extracts author name and photo from matching h-card on the page * Note: Does not yet fetch remote author URLs or follow rel=author links (full authorship algorithm) * Added a `justfile` with recipes for dependency install, testing, and type checking 0.5.2 (2025-07-27) ------------------ * Fixed JSON copy/paste issue in Django admin for h_card field * Changed admin form to use CharField with custom widget instead of JSONField to prevent double-encoding * Added proper JSON formatting and validation in Profile admin interface * Added tests for admin JSON widget functionality 0.5.1 (2025-07-26) ------------------ * Fixed h-card template to properly handle photo data from mf2py parser * Added automatic property name normalization (converts hyphens to underscores for Django template compatibility) * Added webmention integration to use local Profile data when author is a local user * Added automatic synchronization of Profile fields (name, photo_url, url) with h_card JSON data * Added URL and email validation for h_card data to prevent invalid data storage * Enhanced h_card normalization to properly handle nested objects * Added h_card structure validation in admin interface * Fixed all mypy type checking issues * Updated tests for consistency with implementation 0.5.0 (2025-07-25) ------------------ * Added h-card support with Profile model for user profile data * Added flexible JSON storage for all h-card properties * Added h_card template tag for rendering h-card microformats * Added Profile admin interface with JSON editing support * Added h-card parsing and validation utilities * Added comprehensive test suite for h-card functionality * Updated documentation with h-card usage examples 0.4.3 (2025-07-11) ------------------ * Fixed ``webmention_count`` template tag to always return integers for consistent template comparisons * Previously returned string when used directly but integer when used with ``as`` variable assignment * Added comprehensive tests for the webmention_count fix 0.4.2 (2025-07-10) ------------------ * Added Django admin integration for Webmention, Token, and Auth models * Added comprehensive admin test suite * Webmention admin includes filters, search, and organized fieldsets for easy management * Token and Auth admin are read-only for security purposes 0.4.1 (2025-07-10) ------------------ * Fixed Webmention endpoint to return Location header with HTTP 201 status per W3C specification * Added WebmentionStatusView to provide webmention status information at the Location URL * Fixed compatibility with webmention.rocks test suite 0.4.0 (2025-07-10) ------------------ * **MAJOR**: Added complete Webmention support (W3C Recommendation compliance) * Added Webmention model for storing incoming and outgoing webmentions * Added WebmentionEndpoint view for receiving webmentions * Added WebmentionProcessor for validating and parsing webmentions with microformats2 * Added WebmentionSender for discovering endpoints and sending webmentions * Added pluggable interfaces for URL resolution, spam checking, and comment integration * Added Django template tags for displaying webmentions (``webmentions_for``, ``webmention_count``, ``webmention_endpoint_link``) * Added management command ``send_webmentions`` for sending webmentions from the command line * Added comprehensive test suite for Webmention functionality (35 new tests) * Added detailed Webmention documentation with integration examples * Added CSS styling and templates for different webmention types (likes, reposts, replies, mentions) * Added Django signals for webmention processing (``webmention_received``) * Replaced requests library with httpx for better async support and HTTP/2 features * Updated dependencies to use httpx instead of requests 0.3.5 (2025-06-29) ------------------ * Fixed Authorization header handling to check for HTTP_AUTHORIZATION (Django's standard header format) * Maintained backward compatibility with test client Authorization format * Added tests to verify both Authorization header formats work correctly 0.3.4 (2025-06-29) ------------------ * Fixed Token model unique constraint that prevented multiple clients from obtaining tokens for the same user * Removed incorrect unique=True from Token.me field (kept unique_together constraint) 0.3.3 (2025-06-29) ------------------ * Fixed micropub authorization to accept "create" scope (standard Micropub) in addition to legacy "post" scope * Added debug logging for token authentication failures 0.3.2 (2025-06-29) ------------------ * Fixed KeyError in TokenView when 'me' parameter is missing - the token endpoint now correctly handles optional parameters according to IndieAuth spec * Improved token endpoint error responses to use proper IndieAuth error codes (invalid_request, invalid_grant) * Added redirect_uri verification for enhanced security * Implemented one-time use of authorization codes to prevent replay attacks * Added proper content-type headers to token endpoint responses 0.3.1 (2025-06-28) ------------------ * Added merge migration to resolve parallel migration branches 0.3.0 (2025-06-28) ------------------ * **MAJOR**: Implemented fully functional Micropub endpoint with content creation * Added pluggable content handler system for Micropub integration * Added ``MicropubContentHandler`` abstract base class for custom implementations * Added ``InMemoryMicropubHandler`` for testing and development * Added support for both form-encoded and JSON Micropub requests * Implemented Micropub query endpoints (``?q=config``, ``?q=syndicate-to``) * Added comprehensive test suite for Micropub functionality (19 new tests) * Added detailed Micropub documentation with integration examples * Added example content handlers demonstrating various integration patterns * Updated type hints to use modern Python syntax (``list``, ``dict`` instead of ``List``, ``Dict``) * **BREAKING**: Removed old Micropub property methods that were implementation details * Added comprehensive documentation for IndieAuth implementation including consent screen * Added test suite for IndieAuth consent screen functionality (14 new tests) * Fixed MyPy type errors in AuthView for better type safety * Updated development guidelines with "Definition of Done" criteria 0.2.0 (2025-06-16) ------------------ * Fixed Read the Docs build by adding missing dependencies to docs/requirements.txt * Added coverage configuration to exclude migrations from coverage reports * Cleaned up duplicate documentation files (removed outdated .txt versions) * Added type annotations to models.py and views.py * Added mypy configuration with django-stubs for static type checking * Added documentation for running mypy in development.rst * Added comprehensive API reference documentation with examples * Added usage tutorial with client-side implementation examples * Added configuration guide documenting all settings and options * Added concepts documentation explaining IndieWeb protocols with Mermaid diagrams * Updated CONTRIBUTING.rst to reflect current development workflow (uv, ruff, pytest) * Documented Micropub handler architecture (in-memory default plus configurable handler via ``INDIEWEB_MICROPUB_HANDLER``) * Converted all tests from unittest to pytest style * Added __str__ method to Token model * Added docstrings to all model and view classes * **BREAKING**: Removed unnecessary dependencies: - Replaced django-model-utils TimeStampedModel with explicit timestamp fields - Replaced django-braces AccessMixin with direct login redirect - Removed setuptools (not needed at runtime with modern packaging) - Replaced pytz with Python's built-in datetime.timezone.utc * Package now only depends on Django itself 0.1.0 (2025-06-13) ------------------ * Migrated from flit to uv build backend * Moved package from top-level to src layout * Replaced black, isort, and flake8 with ruff * Added Python 3.13 support * Dropped Python 3.9 support (minimum is now 3.10) * Updated pre-commit hooks * Consolidated dev dependencies into single group * Added comprehensive documentation with Sphinx and Furo theme * Updated documentation structure for Read the Docs * Fixed Django settings configuration for tests 0.0.8 (unreleased) ------------------ * Development version (not released) 0.0.7 (2023-01-07) ------------------ * Added migration for auto field * Updated pre-commit hooks 0.0.6 (2022-11-05) ------------------ * Use flit and pyproject.toml instead of setup.py * Support recent Django versions * Even better package infrastructure 0.0.5 (2019-05-19) ------------------ * Auth endpoint works with https://pin13.net/login/ \o/ * Use black for code formatting * Better package infrastructure * Require python >= 3.6 0.0.4 (2016-06-14) ------------------ * exempt csrf checking 0.0.3 (2016-06-13) ------------------ * added migrations 0.0.2 (2016-05-15) ------------------ * Auth and Token endpoints with some tests. 0.0.1 (2016-05-14) ------------------ * First release on PyPI.