Skip to content

JWT Bearer auth crash with php-jwt v6 (JWT::decode(): Argument #3 ($headers) could not be passed by reference) #25

Closed

Description

@Nibot1

Summary

Bearer authentication crashes with a fatal error when using newer firebase/php-jwt versions (v6+):

PHP Fatal error: Uncaught Error: Firebase\JWT\JWT::decode(): Argument #3 ($headers) could not be passed by reference in /usr/ictcore/core/User.php:473

This happens in User::authenticate() during Bearer token decode.

Root Cause

core/User.php uses legacy decode signature:

JWT::decode($access_key, $public_key, array($hash_type));

This is compatible with older firebase/php-jwt (v5), but not with v6+, which expects Firebase\JWT\Key.

Why not pin to v5

Attempting to pin to firebase/php-jwt:^5.5 was blocked by Composer security advisory policy (known advisories on v5.x).
So pinning to v5 is not a safe/viable long-term fix.

Fix

Update to v6-compatible API:

use Firebase\JWT\JWT;
+use Firebase\JWT\Key;
...
-$token = JWT::decode($access_key, $public_key, array($hash_type));
+$token = JWT::decode($access_key, new Key($public_key, $hash_type));

Expected Result

Bearer authentication works with maintained/supported firebase/php-jwt versions (v6+), no fatal error.

Additional Context

Observed in Docker-based deployment of ICTFax, where the auth endpoint and protected routes trigger the Bearer decode path.


Issue drafted with assistance from GitHub Copilot (analysis and troubleshooting steps).

Activity

  1. tahiralmas commented on Sep 7, 2026

    @tahiralmas
    Member

    Confirmed, and thanks for the precise write-up. Your diagnosis is right on both counts: core/User.php used the v5 three-argument JWT::decode(), and pinning back to firebase/php-jwt 5.x is not a real option given the advisories against that line.

    Two things worth adding.

    There were two v5 call sites, not one. Besides User::authenticate(), Forgot_password::token_decode() had the same pattern on the password-reset path, so that one would have fataled next.

    The fix has landed on the php83 branch (PR #23), which moves the whole dependency set off pre-PHP-8 versions and takes firebase/php-jwt to 7.x. Forgot_password was already converted there; core/User.php was the one file the branch had missed, so Bearer auth would have broken the moment that branch merged. Fixed in a2acc98, exactly as you proposed:

    use Firebase\JWT\Key;
    
    $token = JWT::decode($access_key, new Key($public_key, $hash_type));

    JWT::encode() needed no change, since both call sites already pass the algorithm explicitly.

    Leaving this open until #23 merges, then it closes with it. Good catch on a path that would otherwise have surfaced as a fatal in production.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions