Page MenuHomePhabricator

Publish the public key for OAuth 2 access tokens
Open, In Progress, LowPublic

Description

Someone reached out to me recently to ask whether Wikimedia could make its public keys used for OAuth 2 access tokens public. This would allow external apps to verify that an access token is legitimate, and which user it belongs to.

The OAuth 2 spec doesn't say anything about the access token (other than "usually opaque to the client"), but AIUI using a signed but publicly readable access token is not uncommon. ChatGPT says (although I haven't verified it) "Google Cloud, AWS Cognito, Microsoft Entra ID, Auth0, Keycloak, etc., all issue RS256-signed JWT access tokens".

Our OAuth 2 access tokens are HS256 encrypted JWTs with content like this:

[
    "aud" => [
      "14a4471ca53fa4c10d05292e14eae9e6", // OAuth app ID
    ],
    "jti" => "...", // nonce
    "iat" => "2025-06-26 13:11:50.196838 +00:00", // time issued
    "nbf" => "2025-06-26 13:11:50.196841 +00:00", // start of validity
    "exp" => "3025-06-26 13:11:50.193600 +00:00", // expiry
    "sub" => "29601819", // central ID
    "iss" => "https://meta.wikimedia.org", // issuer
    "ratelimit" => [
      "requests_per_unit" => 5000,
      "unit" => "HOUR",
    ],
    "scopes" => [
      "basic",
      "createeditmovepage",
      "editprotected",
    ],
]

As of today, most of the information in the access token is public anyway. I am not sure about the ratelimit - maybe that can be used to identify Wikimedia Enterprise contract tiers? But then the client would know that anyway.

The access token can be exchanged at the OIDC UserInfo endpoint for a plain, unencrypted JSON which shows more information about the user, so making the access token's content public isn't strictly necessary for any client, but might make things simpler.

Event Timeline

Restricted Application added a subscriber: Aklapper. · View Herald Transcript

Pro:

  • Allows creating simple stateless clients which just check the access token rathet than having to call an API (and then probably cache the results of that API call).

Con:

  • There is no way to ensure the timeliness of the information in an access token; e.g. the user might have retracted the authorization represented by the token in the meantime, but a client inspecting the access token rather than calling the UserInfo endpoint wouldn't know that.
  • Puts the burden on clients to get the sometimes tricky details of access token security right (see e.g. the Misuse of Access Token to Impersonate Resource Owner in Implicit Flow section of the OAuth 2 spec).
  • Precludes us from putting secret information (e.g. abuse risk scores) in the access token in the future. The access token is an efficient way to communicate key facts about a client to other parts of the Wikimedia infrastructure (see T394012: Decide how to expose session information outside of MediaWiki).
  • The public keys used to sign access tokens would de facto become an API with stability expectations; chaging encryption keys would become more onerous.
mmartorana changed the task status from Open to In Progress.Jul 7 2025, 2:58 PM
mmartorana triaged this task as Low priority.

@JTweed-WMF pointed out elsewhere that we'll need a mechanism to share JWT keys between various layers of our infrastructure. That could be done via puppet (which is what we do today), but it might be more flexible to do it via a JWKS web endpoint, at which point making it publicly available (e.g. using the conventional .well-known/jwks.json) is not much extra effort.

From a security perspective, this seems reasonable as long as it’s paired with the usual safeguards: short-lived tokens, proper key rotation, minimal claims, and good client validation guidance. It’s a common pattern across the industry.

As I’m not deep in the implementation, I’d also recommend looping in other folks who might be impacted by token handling or infrastructure changes.