wordpress-activitypub icon indicating copy to clipboard operation
wordpress-activitypub copied to clipboard

Feature Request: Upgrade HTTP Message signatures to implement RFC-9421

Open mediaformat opened this issue 7 months ago • 6 comments

What

Following a survey of HTTP Signature support, the SWICG is recommending an upgrading path from the previous draft-cavage-http-signatures-12 to RFC9421

Why

Compatibility

The HTTP Signatures standard has made a few backward-incompatible changes on its path to becoming a full Proposed Standard, [RFC9421]. Many fediverse servers currently handle older versions of the standard and aren't yet compatible with the final version. Here's advice on how to implement HTTP Signatures so as to be compatible with as many different servers as possible.

How

Use the double knock technique

The primary technique we recommend is double-knocking. First, try generating or verifying an HTTP Signature with one version, ideally (but not necessarily) the latest. If the remote server rejects that signature, eg with an HTTP 401 response, or the incoming signature doesn't verify, try with another version. Repeat until a signature passes or you've tried all supported versions. — How to upgrade supported versions

An existing PHP implementation has been developed by Mike MacGirvin, which could be included or used as inspiration (since there is a Guzzle dependency)

mediaformat avatar Jun 12 '25 18:06 mediaformat

Interesting! Yes, the PHP implementation might need some adjustment, WordPress still supports PHP 7.2. Does the library have double-knock built-in or would that need to be built on top of it?

I'm afraid I'm far from being an expert on signatures—would this work be something you'd be interested in leading?

obenland avatar Jun 13 '25 19:06 obenland

Aside from the is the min PHP requirement of 8.1, I see the PSR dependency as a quite bigger issue. There are quite a few tools to downgrade PHP dependencies, but it is not that easy to rewrite the code to support the WordPress request object and the plain PHP one. This is quite a similar problem that spoke against using the ActivityPub php library from @landrok

pfefferle avatar Jun 14 '25 06:06 pfefferle

My thoughts from 2022 https://notiz.blog/2022/05/15/wpsr/ (in German)

pfefferle avatar Jun 14 '25 06:06 pfefferle

The conventional approaches of either building everything from scratch or integrating with a PSR-compliant wrapper each have clear strengths and weaknesses, often leaving developers in a difficult dilemma.

So let’s consider a third, fresh, and efficient alternative that combines the best aspects of both strategies:


📌 Hybrid “Bridge” Approach

The core idea here is to reuse the proven ‘core logic’ of an external library while replacing only the dependency-bound parts with minimal, WordPress-specific code. Instead of implementing the entire PSR stack, we can build a simple bridge that passes only the necessary data for signature generation.


✅ Implementation Steps

1️⃣ Step 1: Isolate and Analyze the ‘Core Logic’

  • Start by reviewing a modern library like macgirvin/HTTP-Message-Signer.
  • Separate the pure cryptographic and signature-generating engine from the parts relying on external HTTP clients like Guzzle.
  • In most cases, the signature engine only needs some string and array inputs like headers, HTTP methods, and body content — it likely doesn’t require the full PSR interfaces.

2️⃣ Step 2: Build a WordPress-Specific ‘Adapter’

  • Instead of a heavy PSR-7 RequestInterface wrapper, create a lightweight adapter class.
  • Its sole job is to extract the data required by the signature engine from WordPress’ native wp_remote_post() response array or a WP_Http object and pass it in a simple format.
  • For example, a WordPressRequestAdapter class might have a method like getHeader($name) that internally fetches headers using WordPress functions, returning them in a format expected by the engine — much simpler than a full PSR implementation.

3️⃣ Step 3: Fork the Library and Namespace Isolation

  • Copy the selected external library into your plugin’s /includes/lib/ or similar directory (effectively a private fork).
  • Remove the composer.json and eliminate all Guzzle or external library dependencies. Refactor the code to use your WordPress adapter from step 2 instead.
  • Most importantly: Use a tool like [PHP-Scoper](https://github.com/humbug/php-scoper) to prefix the entire forked library’s namespace, e.g. WordPress\ActivityPub\Vendor\HttpSigner. This ensures zero conflicts even if other plugins load different versions of the same library.

4️⃣ Step 4: Automate PHP Version Compatibility

  • To handle PHP version differences, apply a tool like [Rector](https://getrector.org/) with a custom rule set to downgrade PHP 8.1 code to PHP 7.2-compatible syntax.
  • This automatically replaces modern features like readonly properties or constructor property promotion with equivalent, older PHP code — far safer and faster than manual refactoring.

🎯 Benefits of This Approach

  • Best of both worlds: Avoids the risks of custom implementation while retaining the reliability of proven, modern libraries.
  • Zero external dependencies: Removes reliance on PSR interfaces or external HTTP clients, making the plugin fully self-contained and WordPress-native.
  • No runtime conflicts: Namespacing with PHP-Scoper ensures complete library isolation, avoiding conflicts even if other plugins use the same library.
  • Safe PHP version support: Automated refactoring with Rector guarantees compatibility with legacy environments, reducing maintenance overhead.
  • Minimal overhead: Focused adapters require less code and less maintenance than full PSR wrappers.

Jiwoon-Kim avatar Jun 14 '25 07:06 Jiwoon-Kim

Certainly! Here is your detailed analysis translated and adapted into clear, professional English:


Yes, of course. The hybrid "bridge" approach I proposed earlier has several advantages, but it is not a perfect solution. There are clear disadvantages and costs that must be considered. It is crucial to accurately understand these drawbacks.

Disadvantages of the Hybrid "Bridge" Approach

1. Initial Setup and Learning Curve Complexity

This approach cannot be completed with a simple composer require command.

  • Requires multiple specialized tools: You need to install, configure, and learn how to use professional tools like Rector and PHP-Scoper. Each tool has its own learning curve.
  • Manual analysis and refactoring: You must analyze and understand the external library’s source code to distinguish between its "core logic" and "dependencies." This initial analysis phase requires significant time and effort.

2. Transfer of Maintenance Responsibility (The Biggest Drawback)

A "private fork" means "maintaining this code is now our responsibility."

  • No automatic updates: Even if the original library developer releases security patches or bug fixes, the code included in our plugin will not be updated automatically. You lose the convenience of composer update entirely.
  • Manual tracking and application: Plugin maintainers must actively monitor changes to the original library (especially security patches) and manually reapply those changes to our modified code (the WordPress adapter and related parts). This is a burdensome and ongoing task.
  • Difficulty with major updates: If the original library undergoes a major structural update, you may have to repeat the entire analysis and refactoring process from scratch.

3. Risk of "Partial Understanding"

While separating the library’s "core logic" from its "dependencies," you might overlook subtle couplings that are not immediately visible.

  • Hidden dependencies: The core logic may implicitly depend on certain behaviors of PSR implementations (e.g., how exceptions are thrown or the order of data processing). If you miss these and build an adapter, unpredictable bugs may appear only in specific edge cases.
  • Code as a black box: If you treat the library as a "black box" and port it without deep understanding, debugging becomes very difficult. When problems occur, it is hard to tell if the issue is with your adapter, the ported core logic, or their interaction.

4. Increased Barrier to Contribution

A complex build process makes it harder for new contributors to join the project.

  • Higher technical requirements: Even potential contributors who want to fix simple bugs must understand not only PHP code but also Rector, PHP-Scoper, and the entire build process. This raises the barrier to entry and can discourage community participation.
  • Centralization of knowledge: Only a few core developers will understand the complex structure, which can harm the project’s overall health.

Conclusion: The Trade-off

In summary, this approach involves a trade-off: you give up Composer’s convenience and automated maintenance in exchange for perfect compatibility and isolation within the WordPress ecosystem.

  • Suitable for: Projects like the ActivityPub plugin, which are highly important, must avoid conflicts with other plugins, and have a core development team willing and able to bear long-term maintenance costs.
  • Not suitable for: Small plugins or solo developer projects, where the initial setup and ongoing maintenance costs may outweigh the benefits.

Therefore, this approach is not a "silver bullet." It is a specialized strategy that should be adopted only after carefully considering the project’s scale, importance, and available resources.

[1] https://www.reddit.com/r/Wordpress/comments/1fp00qq/is_it_time_for_a_fork_a_reasonable_roadmap_for_a/ [2] https://wordpress.org/support/topic/update-plugins-gets-stuck-in-maintenance-mode/ [3] https://developer.wordpress.org/plugins/wordpress-org/common-issues/ [4] https://github.com/WordPress/plugin-check-action/issues/89 [5] https://wordpress.stackexchange.com/questions/15379/how-to-fork-a-plugin [6] https://medium.com/@mkey_dev/wp-content-io-modern-wordpress-plugin-deployment-made-simple-7843ff7b3aee [7] https://wordpress.stackexchange.com/questions/154359/how-to-fork-a-plugin-to-avoid-updates-after-modifications [8] https://wpservices.com/the-hidden-challenges-of-wordpress-maintenance-for-agencies-and-how-to-overcome-them/

Jiwoon-Kim avatar Jun 14 '25 07:06 Jiwoon-Kim

📄 Proposal: Modernizing WordPress HTTP Handling for Interoperability with PSR-based Libraries

Background

The current WordPress HTTP API (wp_remote_get(), wp_remote_post(), etc.) has served the platform reliably for years. However, its proprietary structure deviates from widely adopted PHP standards like PSR-7 (HTTP Message Interfaces) and PSR-18 (HTTP Client Interfaces), limiting interoperability with modern PHP libraries and frameworks.

This gap becomes increasingly problematic when integrating libraries such as macgirvin/HTTP-Message-Signer or lcobucci/jwt that natively rely on PSR-7 request/response objects or PSR-18 clients. Developers are forced to either fully reimplement these libraries or create cumbersome wrappers and compatibility bridges.


Current Challenges

  • No native PSR-7 or PSR-18 support in WordPress core.
  • Legacy internal HTTP abstraction (WP_Http, wp_remote_*) based on array responses.
  • Difficult interoperability with modern PSR-compliant PHP libraries.
  • Risk of namespace and dependency conflicts when bundling external libraries inside WordPress plugins.
  • Compatibility constraints due to WordPress’s wide PHP version support range (7.0+).

Short-term Solution: Hybrid Bridge Approach

As an immediate and pragmatic workaround, we propose a Hybrid Bridge pattern that isolates external libraries' core logic while substituting PSR-specific dependencies with minimal, WordPress-friendly adapters.

Key steps:

  1. Analyze and extract core logic from external libraries, decoupling cryptographic/signature algorithms from their HTTP layer dependencies.
  2. Create lightweight WordPress adapters that translate wp_remote_* responses and requests into minimal PSR-like data structures containing only essential properties (method, headers, body).
  3. Fork and namespace-isolate libraries within the plugin (e.g., WordPress\ActivityPub\Vendor\HttpSigner) to prevent conflicts with other plugins or themes using different versions.
  4. Automate PHP version compatibility adjustments using tools like [Rector](https://getrector.org/), ensuring code runs on both legacy and modern WordPress environments.

This ensures that ActivityPub or other modern plugins can safely leverage modern cryptographic and HTTP signing libraries without introducing conflicts or external dependencies.


Long-term Proposal: Refactor WordPress HTTP API

To address the structural limitations sustainably, we propose a mid-to-long term plan to:

  • Introduce native PSR-7 and PSR-18 support within WordPress core or at least provide optional interfaces for plugin developers.
  • Gradually deprecate proprietary HTTP abstractions like WP_Http in favor of modern, standard-compliant interfaces.
  • Maintain backward compatibility by offering a bridging layer between the legacy API and new PSR-based classes.
  • Foster a community-driven working group to evaluate, implement, and document this transition.

Benefits

  • Improved interoperability with modern PHP libraries and frameworks.
  • Simplified plugin development for third-party integrations.
  • Future-proofing WordPress core for evolving PHP standards.
  • Cleaner, more maintainable codebases across the ecosystem.
  • Easier upgrades and dependency management for plugin authors.

Conclusion

While the Hybrid Bridge approach offers an effective immediate workaround for integrating PSR-dependent libraries in WordPress, it remains a temporary solution. A forward-looking initiative to modernize the WordPress HTTP API — aligning it with widely adopted PHP standards — will ultimately benefit the platform's extensibility, performance, and developer experience.

We invite the WordPress core team and developer community to discuss and explore the feasibility of this long-term roadmap.

Jiwoon-Kim avatar Jun 14 '25 07:06 Jiwoon-Kim