Skip to content

Separate inbox authentication from the declared activity actor #1290

Description

@dahlia

Motivation

#1289 reports that Mastodon sends FEP-7aa9 FeatureRequest activities without an actor. Fedify rejects them before an inbox listener runs, even when their Linked Data Signatures have been verified. The planned patch for 2.4 will handle this case specifically.

The patch addresses FeatureRequest, but other requests without an actor would still need activity-specific exceptions. Fedify could identify authenticated principals independently of the payload's actor and expose them to applications for permission checks. This would avoid adding another core exception whenever an extension uses a request without an explicit actor.

Signature validity alone is not enough. An attacker can sign a request that names someone else's collection as its instrument. For FEP-7aa9, authorization also requires checking that the authenticated requester owns the collection. Inferring the requester from the collection's attributedTo without comparing it with an authenticated principal does not establish that link.

Proposed approach

Separate inbox authentication from the activity's declared actor and from application-level permission checks. The API is still undecided. It should:

  • Retain the authenticated principals and authentication mechanisms, after verifying both the signatures and the keys' ownership or controller relationships. Distinguish the HTTP request signer from the signers of the activity document, since a server can forward a document signed by another actor. Handle multiple Object Integrity Proofs without arbitrarily choosing one signer.
  • Preserve the existing authentication requirements for activities with an explicit actor. Do not bypass a failed authentication check against the declared actor by falling back to a different principal.
  • Allow requests without an explicit actor to reach handlers when an authenticated requester can be identified under the activity's protocol. This must not waive actor requirements or permission checks imposed by the activity's protocol.
  • Make that authentication information available to inbox listeners and interaction helpers. For example, featureInteraction should be able to compare the authenticated requester with the owner of a reliably resolved FeaturedCollection.
  • Preserve the original signed payload and retain the authenticated principals, verified key references, and authentication mechanisms through queued processing. Do not add an inferred actor to the document as though it had been covered by the original signature.

Regression coverage should include direct delivery and forwarding, mismatched collection owners, multiple proofs, and queue retries. Successful signature verification with no usable authenticated principal must not be enough to dispatch a request to an inbox listener.

Release scope

Target the next minor release. Changes to the shared authentication flow and listener APIs need broader review than the focused maintenance fix for #1289. Keep the 2.4 patch focused on #1289 and reconcile it with the shared authentication changes when those are ready.

Activity

  1. added this to the Fedify 2.5 milestone on Oct 10, 2026
  2. dahlia commented on Oct 10, 2026

    @dahlia
    MemberAuthor

    PR #1291 marks its temporary FeatureRequest authentication and queue handling with FIXME/TODO comments. When implementing #1290, remove or replace those paths, including the signature-observation scan, inferred actor injection and featureRequestActor queue metadata/replay. Retain collection ownership validation and preserve the original signed payload.

  3. dahlia commented on Oct 10, 2026

    @dahlia
    MemberAuthor

    The review of #1291 exposed security requirements that the shared authentication design here should preserve:

    • Bind authenticated principals to the activity interpretation used during verification. A queued worker must not re-fetch a mutable JSON-LD context and dispatch different object or instrument values under the producer's authentication result. Comparing only instrument was insufficient. This applies to HTTP-authenticated requests as well as signed activity documents.
    • Keep the original signed payload separate from the internal parsed view. Forwarding must preserve the original document, while queue processing and retries must retain the verified interpretation and authentication metadata. An inferred actor must not be presented as part of the signed payload.
    • Preserve collection provenance checks when resolving ownership. For web IDs, check the final document's origin against the collection ID before trusting attributedTo; a cross-origin redirect must not let another host claim ownership. Portable collections need their existing proof-based identity checks. The resolved owner must still match an authenticated principal.

    The maintenance patch now queues a producer-frozen parsed view under built-in contexts for actorless FeatureRequest activities. When replacing the #1290-linked FIXME/TODO paths, reassess this snapshot together with featureRequestActor metadata and inferred-actor restoration as part of shared principal replay. The temporary implementation need not become the general API, but retain its regression coverage for changing contexts, retries, original payload/proof preservation, and collection redirects.

  4. dahlia commented on Oct 10, 2026

    @dahlia
    MemberAuthor

    Another regression from #1291 concerns property cardinality: instrumentIds omits embedded objects without an ID. A request can therefore have two instrument values but only one instrument ID. If the anonymous object comes first, the singular instrumentId is also null, even though the previous owner-resolution code authenticated the collection named by the second value.

    When implementing this issue, validate the full property's cardinality and the selected value before inferring a requester or checking permissions. ID accessors alone do not establish that the property contains exactly one value. Authentication and listeners must use the same instrument; retain the regressions covering anonymous values both before and after the collection reference.

  5. dahlia commented on Oct 10, 2026

    @dahlia
    MemberAuthor

    Two further lessons from #1291 should carry into the shared authentication design.

    The earlier cardinality issue also affects attributedTo: typed parsing can discard untyped anonymous values, so even counting the parsed objects is insufficient. Validate the full expanded property before typed parsing, and use that same expanded view for parsing and ownership checks. Retain regression tests for anonymous values both before and after the owner reference.

    Keep authentication/authorization rejection separate from invalid remote data and transient lookup failures. Malformed documents, unusable redirect responses, redirect cycles, and exceeded redirect limits should not trigger retries; transport failures, timeouts, HTTP 408/429, and 5xx responses should remain retryable. Consider structured loader failure reasons when implementing the shared flow, rather than carrying the patch's message-based classifier into the general API. Retain #1291's regression tests for both built-in document loaders, including HTTP redirects and alternate document links.

  6. dahlia commented on Oct 10, 2026

    @dahlia
    MemberAuthor

    Another #1291 finding affects the actorless exception's entry condition for FeatureRequest: an empty actorIds list does not mean the activity has no actor values. Anonymous actors, blank-node references, and untyped embedded values can disappear from ID accessors or typed parsing. Inferring a collection owner in that case replaces an explicitly supplied actor in the internal view.

    When implementing this issue, distinguish an absent actor from an explicit actor that cannot be authenticated. The latter must not gain access through actorless principal fallback. Preserve enough of the expanded input to make that distinction, bound to the same context interpretation used for parsing and verification. #1291 records the contexts used by each parsing attempt to check actor presence without re-fetching mutable contexts; this temporary mechanism should be replaced with shared verified-view metadata. Retain the anonymous/blank-node actor regression tests across authentication methods and direct/queued delivery.

    Allowing private network addresses must not disable HTTP(S) validation of document fetch targets; #1292 tracks that loader fix and its backports.

  7. dahlia commented on Oct 10, 2026

    @dahlia
    MemberAuthor

    Two more parsing details from #1291 should inform this issue. Expanded property arrays can contain a single @list node that holds multiple values. Cardinality checks must use the generated decoder's list-container interpretation, so anonymous values are counted before typed decoding drops them. Retain tests for a list with an anonymous attribution before or after the owner, and for a valid single-owner list.

    Decoding a whole collection also decodes embedded objects unrelated to ownership. For example, a malformed Link.hreflang in an attachment makes the parser throw RangeError, just as a malformed temporal literal does. #1291 identifies this case using the existing expanded document and the generated decoder's language-tag normalization; RangeError exceptions from document/context loaders remain retryable. The general design should distinguish document decoding failures from loader failures at their source, so new parser fields do not require more ad hoc exception checks in ownership resolution. Consider whether ownership resolution needs to decode unrelated attachments at all. Any change there must keep the existing validation and verification guarantees.

  8. dahlia commented on Oct 11, 2026

    @dahlia
    MemberAuthor

    The portable collection path in #1291 still exposes a source-data gap: lookupObject() returns a typed object, so serializing that result cannot recover untyped attribution values omitted during decoding. A resolver-level test confirms that the current one-owner check accepts this lossy result; that test isolates the post-lookup boundary and does not exercise proof verification. The general design needs access to the expanded collection view used by portable verification, rather than a second expansion or a typed round-trip.

    Malformed multibase data also shows why enumerating decoder exceptions does not scale: a collection with an embedded DataIntegrityProof and an unsupported proofValue prefix raises plain Error, whereas malformed dates and language tags raise RangeError. The design should record which operation failed and keep loader failures separate from decoding failures. Diagnostics must inspect the same expanded view that failed; re-expanding through a changing remote context can conceal a malformed temporal literal.

  9. dahlia commented on Oct 11, 2026

    @dahlia
    MemberAuthor

    Release-plan update: the narrow maintenance fix in #1291 was closed without merging, and #1289 is now targeted for Fedify 2.5 alongside this issue. This supersedes the issue body's plan to keep a separate 2.4 patch. Earlier comments describing FIXME/TODO cleanup refer to the unmerged PR branch; that code will not ship, so those cleanup instructions do not apply to released code.

    The review loop exposed a design problem with the bridge: it tried to reconstruct authorization data from typed objects after decoding had discarded source values, and to classify decoding failures by enumerating exception types/messages. Additional patches also had to bind each verification attempt to its parsed view and preserve that view across queues. A type-specific exception did not avoid the need for shared authentication foundations. The passing CI suite did not cover the remaining counterexamples.

    Carry these requirements into the design:

    • Bind principals and verified key ownership/controller relationships to the exact document interpretation and verification attempt that established them. Distinguish HTTP request signers from document signers, handle multiple proofs, and do not reuse successful observations from an attempt whose parsed result was discarded.
    • Preserve the explicit-actor policy. An empty actorIds accessor is not proof that actor is absent: anonymous actors, blank nodes, and untyped values must not gain access through principal fallback. A successful parse with no proofs is not authentication.
    • Retain the complete expanded values needed for authorization before typed decoding. Account for @list containers and anonymous values before or after the collection/owner reference. Portable collection lookup needs the source view used by proof verification, not a reconstruction from lookupObject()'s typed result. The latest portable regression isolated the post-lookup boundary; it did not test an end-to-end proof bypass.
    • Resolve collection ownership from an authoritative resource, not a sender-supplied embedded instrument or WebFinger account discovery. Preserve final-origin checks for web collections and proof-based identity checks for portable ones, then compare the owner with an authenticated principal.
    • Keep the original signed payload separate from the verified view and authentication metadata. Queue workers and retries must use the authenticated interpretation without reloading mutable contexts. Do not present a synthesized actor as signed data.
    • Distinguish decoding failures from loader failures where they occur. Malformed temporal/language/multibase literals can throw different exception classes; a list of per-field checks or error-message matches will keep growing. Diagnostics must use the expansion that failed. Assess whether ownership resolution needs to decode unrelated attachments, while preserving validation and verification guarantees. Keep transport failures, timeouts, HTTP 408/429, and 5xx responses retryable.

    Use #1291's tests and review counterexamples as a regression inventory, reproducing each claim before accepting it. Retain valid-input tests as well as rejection tests, cover authentication fallbacks, forwarding, and direct and queued delivery, and test supported runtimes. Reimplement the flow around these requirements rather than porting the temporary bridge. #1292 tracks the independent HTTP(S) scheme-validation bug and its maintenance backports.

  10. self-assigned this
    on Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Fields

Priority

None yet

Effort

None yet

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions