Page MenuHomePhabricator

Support JWT generation for session tokens in MediaWiki core
Closed, ResolvedPublic

Description

Provide a core utility method for generating a JWT.

We don't necessarily have to centralize this (OAuth 2 JWT generation happens many layers deep in third-party code, so probably that won't be updated to use the new mechanism), but for T399200: Update existing cookie-based sessions to include JWT cookie we'll need to generate JWTs either in core or in CentralAuth, and then core seems nicer since we are kind of trying to standardize JWT-based sessions.

We'll need to pick a JWT library (probably lcobucci/jwt since it's one of the most popular PHP JWT libraries and we already use it in OAuth 2, but if we really want to reconsider that choice, we could), add it to vendor, and provide a wrapper mechanism somewhere in the session component. That could be minimal (just a generic wrapper so we can more easily swap the underlying implementation) or it could be more opinionated about the JWT format (automatically invoke the new method defined in T399198: Define standard JWT session data for supported session types, take a User and handle the formatting of the sub field etc).

Event Timeline

Restricted Application added a subscriber: Aklapper. · View Herald Transcript
Tgr renamed this task from Support session JWT generation in MediaWiki core to Support JWT generation for session tokens in MediaWiki core.Jul 10 2025, 8:49 PM

Major JWT libraries (from searching for the "jwt" label on Packagist and ignoring anything with <100 stars):

(Ignored libraries which are marked as unmaintained (namshi/jose), or very closely integrated with a framework (tymondesigns/jwt-auth, lexik/LexikJWTAuthenticationBundle, SiZEg/yii2-jwt.)

Stats:

Librarylcobucci/jwtfirebase/php-jwtweb-token/jwt-libraryReallySimpleJWTadhocore/php-jwt
Major contributors in last 2 years21111
Packagist installs271,233,682379,958,72415,160,0472,039,3791,305,259
Packagist dependents9482,099672318
Github stars7,4819,658930289298
Github forks6001,4251155520
LoC2.7K1.8K12K4K1K
# of commits in last year13214001
Release frequencyabout twice a yearabout once a yearfrequent small releaseslast in 2022mostly stopped in 2021
LicenseBSD-3-ClauseBSD-3-ClauseMITMITMIT
% of PRs from last year closed92%58%88% (doesn't get many)weren't any100% (1 out of 1)
Security advisories1 medium2 critical---

Feature support according to jwt.io:

lcobucci/jwtfirebase/php-jwtweb-token/jwt-libraryadhocore/php-jwt
Screenshot Capture - 2025-07-28 - 00-21-25.png (1×852 px, 146 KB)
Screenshot Capture - 2025-07-28 - 00-21-18.png (1×848 px, 153 KB)
Screenshot Capture - 2025-07-28 - 00-21-45.png (1×852 px, 147 KB)
Screenshot Capture - 2025-07-28 - 00-21-58.png (1×850 px, 152 KB)

(does not list ReallySimpleJWT)

lcobucci/jwt:

  • Seems stable (no changes in last year other than CI / PHP version upgrade)
  • Added PHP 8.4 support during the RC period

firebase/php-jwt:

  • A fork of the old PEAR JWT package.
  • Seems less stable (e.g. ES512 support was only added a year ago)
  • rate limit?

web-token/jwt-library:

  • A monorepo of some sort? (web-token/jwt-library vs web-token/jwt-framework) The parent repo is actively maintained, the JWT library had no activity for a little over a year, but it's just the copy of a directory in the parent repo.
  • Includes a Symfony bundle but otherwise doesn't seem Symfony-specific.
  • Seems like a single developer passion project. The author does have a ton of security-related web standards libraries though. They maintain web-auth/webauthn-lib which we use in the WebAuthn extension.
  • Pulls in a lot of Symfony dependencies.

RobDWaller/ReallySimpleJWT:

  • Seems abandoned, issues / PRs since 2023 have no activity from the author, and there are no commits in the last 3 years.
  • PSR-7 / PSR-15 compatible.

adhocore/php-jwt:

  • Seems mostly inactive since 2021 although PRs from others are still merged and released.

Apparently we use both firebase/php-jwt and lcobucci/jwt in the OAuth extension - the first is used for our homegrown OIDC implementation in OAuth 1, the second is a dependency of oauth2-server which we use for OAuth 2.

I think that's enough to exclude ReallySimpleJWT and adhocore/php-jwt from further consideration. They are much less popular, both are kinda unmaintained, adhocore/php-jwt doesn't support elliptic keys, ReallySimpleJWT is not even registered with jwt.io. adhocore/php-jwt is really small, which would be an advantage for security review, but lcobucci/jwt and firebase/php-jwt are already in MediaWiki vendor.

Librarylcobucci/jwtfirebase/php-jwtweb-token/jwt-library
PHP compatibility8.3-8.4, CI is run on nightlies; 5.2.0 (last April) is the last version with PHP 8.1 support8.*8.1+
Composer dependenciesOpenSSL and Sodium PHP extensions-JSON, mbstring PHP extensions; Symfony console, http-client; spomky-labs/pki-framework; brick/math; a compat library for Sodium
JWT protocol supportSupports everything except RSASSA-PSS (which is supported via an add-on)no support for RSASSA-PSS, large elliptic keys, sub/aud/jti checksupports absolutely everything
JWK supportnoyesyes
ArchitectureDI, builder patterns, extensible in many ways.Two static endpoints to encode / decode, with static flags for configuration. Not really extensible.DI, extensive builder patterns, extensive. Seems to have a bit of a "build it yourself" approach, with a lack of sensible defaults.
InteroperabilityCan use PSR-20 for clockCan use PSR-6/7/17 compatible libraries for JWK checksPSR-7/17/20
Error handlingThrows a custom exception which returns a set of ConstraintViolation objects, Robust, if a little cumbersome.Throws a bunch of different exceptions, some standard, some custom, does not look greatThrows custom exceptions, in a reasonable class tree.
DocumentationHas decent high-level documentation, seems easy to use. No detailed class/method docs.Has a decent readme file and basic class/method inline docs.Has a doc page but it's not very readable; and basic class/method inline docs.
CILooks extensive. Mutation testing, performance testing, static analysis, B/C testing, unit tests with wide coverage, run on all supported PHP versions + nightlies.Unit tests and static analysis, coverage looks decent.In the monorepo; covers the encryption algorithms but not much testing for the rest of classes

So I think web-token/jwt-library is out. It's more of an academic project, very wide range of features but engineering-wise it's not great (lots of unnecessary dependencies, limited tests, weird git workflow, docs aren't great). And the other two are already in vendor (so no security review needed).

Between the remaining two, lcobucci/jwt is better written, has better docs and tests. I think there are two arguments against it

  • lack of JWK support (which per T397924: Publish the public key for OAuth 2 access tokens maybe we'll need in the future);
  • it's a dependency of a dependency of the OAuth extension, which is a package management pain point (our Composer declarations will have to be kept compatible with the Composer declarations of a third-party library).

Also there's an open task suggesting there's no JWE support, but then it supports symmetric-key algorithms... I'm not sure what's the difference?

Search for the relevant libraries. Turns out firebase/php-jwt is used in ContentTranslation (for authenticating with cxserver) and CheckUser (for paging-related URL parameters, to prevent data leak).

In general, since different libraries all encode/decode tokens according to the same specification, it shouldn't be hard to switch libraries (both for core if we change our minds about the choice of library later, and in extensions if we want to avoid having two different JWT libraries).

Going through the potential drawbacks for lcobucci/jwt:

  • Composer version conflicts: we use a fork of oauth-server that's (I think) slightly pre-9.0 (a somewhat bad situation on its own: T261462: Migrate OAuth extension back from wikimedia/oauth2-server fork to upstream). oauth-server 9.0 and all of its later versions require lcobucci/jwt 5.0+, but our fork requires 3.4+ - 4.*, and mediawiki/vendor pins 4.1.5. (Probably because 5.* requires PHP 8.1+?) Reviewing the differences between 4.1.5 and latest, they don't seem problematic. There are some breaking changes in 5.0 but they are quite minor; we'll need to upgrade at least to 5.1 soon anyway (first version that supports PHP 8.3). So I think this is not a big problem. And the fork situation is something we should fix anyway.
  • Lack of JWE support (which would allow making the contents of the JWT secret): asked some SREs at a meeting, no one thinks we'll need them. If we change our minds, it's not hard to switch libraries.
  • Lack of JWK support (which would allow sharing the public keys between parts of the intra / third parties in a standard way): not sure yet if we'll end up using JWKs. If we do, the lack of support is not ideal, but can be added separately (e.g. the JWK class of firebase/php-jwt can process a JWKS structure and return a format that's usable by lcobucci/jwt) - that would again mean using multiple libraries, but since that's already the status quo, it's not that bad. Alternatively, we could add support to lcobucci/jwt (the author is open to that).
  • Lack of RSASSA-PSS support: seems unlikely that we'd care. RSASSA-PSS signatures are non-deterministic, but JWTs contain non-deterministic data anyway (since the issuance timestamp is included) so it wouldn't really make a difference.

So I'll tentatively go with lcobucci/jwt. There should be an opinionated wrapper around it that 1) makes it easy to switch libraries in the future, 2) makes it easy for the site administrator to make decisions about encryption algorythm etc. without extensions using JWTs having to care about it.

Change #1175261 had a related patch set uploaded (by Gergő Tisza; author: Gergő Tisza):

[mediawiki/core@master] Add lcobucci/jwt to Composer requirements

https://gerrit.wikimedia.org/r/1175261

Change #1175273 had a related patch set uploaded (by Gergő Tisza; author: Gergő Tisza):

[mediawiki/core@master] jwt: Add wrapper class for using lcobucci/jwt

https://gerrit.wikimedia.org/r/1175273

Change #1175261 merged by jenkins-bot:

[mediawiki/core@master] Add lcobucci/jwt to Composer requirements

https://gerrit.wikimedia.org/r/1175261

Change #1175273 merged by jenkins-bot:

[mediawiki/core@master] jwt: Add wrapper class for using lcobucci/jwt

https://gerrit.wikimedia.org/r/1175273

Change #1185286 had a related patch set uploaded (by Reedy; author: Reedy):

[mediawiki/core@master] RELEASE-NOTES-1.45: Mention lcobucci/jwt

https://gerrit.wikimedia.org/r/1185286

Change #1185286 merged by jenkins-bot:

[mediawiki/core@master] RELEASE-NOTES-1.45: Mention lcobucci/jwt

https://gerrit.wikimedia.org/r/1185286

lcobucci/jwt implements EdDSA via libsodium and other encryptions via openssl, which leads to annoying differences in key management. lcobucci/jwt#862 has an example of how to do it.

Change #1190624 had a related patch set uploaded (by Gergő Tisza; author: Gergő Tisza):

[mediawiki/core@master] session: Fix date handling for JWT cookies

https://gerrit.wikimedia.org/r/1190624

Change #1190624 merged by jenkins-bot:

[mediawiki/core@master] session: Fix date handling for JWT cookies

https://gerrit.wikimedia.org/r/1190624

Change #1190712 had a related patch set uploaded (by Gergő Tisza; author: Gergő Tisza):

[mediawiki/core@wmf/1.45.0-wmf.19] session: Fix date handling for JWT cookies

https://gerrit.wikimedia.org/r/1190712

Change #1190713 had a related patch set uploaded (by Gergő Tisza; author: Gergő Tisza):

[mediawiki/core@wmf/1.45.0-wmf.20] session: Fix date handling for JWT cookies

https://gerrit.wikimedia.org/r/1190713

Change #1190712 merged by jenkins-bot:

[mediawiki/core@wmf/1.45.0-wmf.19] session: Fix date handling for JWT cookies

https://gerrit.wikimedia.org/r/1190712

Change #1190713 merged by jenkins-bot:

[mediawiki/core@wmf/1.45.0-wmf.20] session: Fix date handling for JWT cookies

https://gerrit.wikimedia.org/r/1190713

Mentioned in SAL (#wikimedia-operations) [2025-09-23T20:28:24Z] <tgr@deploy1003> Started scap sync-world: Backport for [[gerrit:1190712|session: Fix date handling for JWT cookies (T399243 T399200)]], [[gerrit:1190713|session: Fix date handling for JWT cookies (T399243 T399200)]]

Mentioned in SAL (#wikimedia-operations) [2025-09-23T20:55:42Z] <tgr@deploy1003> tgr: Backport for [[gerrit:1190712|session: Fix date handling for JWT cookies (T399243 T399200)]], [[gerrit:1190713|session: Fix date handling for JWT cookies (T399243 T399200)]] synced to the testservers (see https://wikitech.wikimedia.org/wiki/Mwdebug). Changes can now be verified there.

Mentioned in SAL (#wikimedia-operations) [2025-09-23T21:10:09Z] <tgr@deploy1003> Finished scap sync-world: Backport for [[gerrit:1190712|session: Fix date handling for JWT cookies (T399243 T399200)]], [[gerrit:1190713|session: Fix date handling for JWT cookies (T399243 T399200)]] (duration: 41m 51s)

Change #1191158 had a related patch set uploaded (by Gergő Tisza; author: Gergő Tisza):

[mediawiki/core@master] RELEASE-NOTES-1.45: Mention JwtCodec

https://gerrit.wikimedia.org/r/1191158

Change #1191158 merged by jenkins-bot:

[mediawiki/core@master] RELEASE-NOTES-1.45: Mention JwtCodec

https://gerrit.wikimedia.org/r/1191158