For T398815: WE5.1.2 Verifiable MediaWiki sessions, we want to define a standard data structure to put inside session JWTs. We'll probably want to do this in MediaWiki core, so it's easy to use in any session provider.
It could take the form of a utility method like SessionManager::getJwtData(), which would use a hook to allow extensions / sites to add additional data.
Currently, the data contained in Wikimedia OAuth 2 access tokens looks like this:
- Standard JWT fields
- iss (issuer) - wiki origin URL
- sub (subject) - central user ID (the CentralAuth ID for SUL wikis, the local user ID for other wikis)
- aud (audience) - OAuth app ID
- jti (JWT ID) - unique ID, which can be used to prevent replay attacks
- iat (issued at) - timestamp of when the JWT was generated
- exp (expiration time) - timestamp of token expiry (for OAuth 2 owner-only consumerts, this is 1000 years after issuance; for normal consumers, it's 4 hours after issuance)
- nbf (not before) - start of validity. Not really used, we set it the same as iat
- Extra fields
- scopes - list of permission bundles associated with the session (OAuth 2.0 calls these scopes; OAuth 1.0 and MediaWiki core calls them grants). This is the same data that's used for SessionProvider::getAllowedUserRights() but it's informational (MediaWiki will load it from the DB rather than relying on the JWT)
- ratelimit - rate limiting information for Envoy, in the form of [ "requests_per_unit" => 1234, "unit" => "HOUR" ]
For the purposes of rate limiting, this is approximately right, but probably could use small improvements:
- For SUL wikis, the issuer should probably be Meta (the designated OAuth / SUL wiki) rather than the current wiki?
- We should probably namespace the user ID (so two users at two different non-SUL wikis, or a SUL wiki and a non-SUL wiki, don't end up with the same sub value). The spec says The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique. so technically we are not violating it (since the issuer is the wiki URL, and for a given wiki user IDs can't conflict), but for the purposes of rate limiting, globally unique values would probably be nicer? So maybe we should have something like centralauth:<user id> (SUL) / <wiki id>:<user id> (non-SUL).
- Similarly, we might want to namespace OAuth app IDs. This is trickier as the OIDC spec requires this to be exactly the OAuth client ID, so we'd have to namespace those, which would be very disruptive. It's a multi-value field though, so we could have one value that fulfills the spec, and another that's used internally.
- Also, since the goal is developer authentication, maybe we'll also want something here that's more descriptive than a client ID, such as the developer's user ID?
- Should we put something in aud for cookie-based sessions (which aren't associated with an application / developer)? The JWT spec doesn't require the field to always be present, but maybe convenient to put the user ID there?
- For rate limiting, rather than encoding the specific numbers in the JWT (which than cannot be changed, potentially forever in the case of owner-only consumers), we probably want some sort of rate limit classes?