Page MenuHomePhabricator

[SPIKE] Add security scheme information to MediaWiki REST API description
Open, In Progress, MediumPublic5 Estimated Story Points

Description

Describe at least two of the most commonly used API security mechanisms in the security schemes object of the MediaWiki REST API OpenAPI description.

  • Suggestion: CSRF, OAuth 2.0, MW Session

For more information, see the parent task: T422484.

Event Timeline

Notes from estimation:

  • Some of our security options are contained in extensions, which may or may not be installed.
  • Let's start with CSRF + MW Session since we know they will be available everywhere; we will need to have a more robust solution for OAuth that checks for installed extensions and injects appropriately, which will require a framework change.
  • Recommendation to update this to at least two tickets: Start with a spike for how to do this, then implement for MW OAD, and then assume that the linting rules will be a separate task.
  • MWP is also working on OAuth right now.
  • Next step: Hash this out in tech discussion on Tuesday.

Other notes for estimation that came up during a meeting with @BPirkle and @KineticPelagic:

  • ModuleSpecHandler is the class which contains the logic to create the spec file. Inside it, we will need to implement the logic to populate the securitySchemes object within components, as well as the top-level or per-operation security field.
  • The CSRF token is not passed as a header but in the request body (see TokenAwareHandlerTrait::getTokenParamDefinition()), so it cannot be modeled as a standard apiKey in header — this will need some thought on how to represent it correctly in OpenAPI 3.0.
  • The token is defined as PARAM_REQUIRED => false in the body schema, but validateToken() will throw a 403 if it's missing and the session provider is not CSRF-safe. This means the requirement is conditional on the authentication method used, not on the operation alone — which is another reason why modeling it as a security scheme (rather than a body parameter) is the right approach.
  • On write endpoints, the security requirement should be expressed as an OR between two options: (MW Session cookie AND CSRF token) or (OAuth2). In OpenAPI 3.0 this maps to an array of security requirement objects.

cc. @KBach, @HCoplin-WMF

KBach renamed this task from Add security scheme information to MediaWiki REST API description to [SPIKE] Add security scheme information to MediaWiki REST API description.Thu, Apr 23, 2:35 PM
HCoplin-WMF set the point value for this task to 5.Thu, Apr 23, 3:08 PM

Notes from estimation:

  • At a framework level, this will require iterating through extensions to define and recognize what is available for a given installation.
  • There are a lot of unknowns about how exactly this will work; this is scoped as the research spike to proceed with more confidence!

Two ways of doing this come to mind:

Narrow

One option is to focus on just the security schema. Rather than focusing on specific extensions or authentication types, I'd suggest looking at the SessionProvider class and SessionProviderInterface. The SessionManager::getProviders() function will return all configured session providers for a particular wiki. Extensions that provide authentication mechanisms mark themselves as session providers (here's where OAuth does it).

The idea is that the OpenAPI spec generation code would iterate the available session providers on the wiki and interrogate each to find out how to list it in the security schemes object. Hopefully the session provider already makes the necessary information available (SessionProviderInterface::safeAgainstCsrf() is probably relevant, as that determines if a CSRF token is needed or not). If all the necessary info is not available, we could consider adding it (adding stuff to an existing interface is a little tricky, but there are always ways to approach the problem).

Wide

Alternatively, we could consider the security scheme need as just one example of a more general challenge: what happens if something an extension does modifies how the OpenAPI spec should be generated? I'm not sure whether or not we'll run into any other cases of that, but if so, we currently don't have a great way to handle it. We could consider adding a hook allowing extensions to fiddle with the fully-generated OpenAPI spec (as a PHP array) before it is returned. That'd solve this entire class of need, at the cost of being something of a blunt tool. New hooks shouldn't be added lightly, and they're not always a great design pattern. I suspect that doing this for security schemes would lead to a decent amount of copy-paste code across extensions. But I bring it up as an option just to make sure we've considered it.

Recommendation

If we can get all the necessary information from the existing session-related classes, I'd recommend going that way. That would be non-intrusive, fairly straightforward, and wouldn't have ripple-effect changes or be hard to reverse if we later change course.

If the necessary information is not available, then we should consider what would be needed to add it and then decide how we feel.

@aaron , am I missing any better ideas about how to do all this?

In general I think the spec of an API module should be the same no matter where that module is installed. Extensions changing APIs provided by other components is something that proved problematic in the action API. It also makes versioning less meaningful. Ideally, the spec should tell a client what it can expect from an API module, no matter on which domain it is called.

That said, the security scheme is probably a notable exception to this rule. It's really not a property of the API but of the site, but OpenAPI doesn't quite model it that way.

So I agree with Bill: Focus on the security use-case, get the relevant information from the authentication manager / session provider, and inject it into the spec. And try to keep it as isolated as possible.