Page MenuHomePhabricator

Define basic workflows
Closed, ResolvedPublic

Description

This task provides examples of basic workflows for the API Portal.

To do: Review with stakeholders and finalize

Anonymous requests (No OAuth flow)
  • Read-only
  • For evaluation and prototyping
  • Anonymous requests have a lower rate limit than requests that use an OAuth flow.
# Read requests
GET https://api.wikimedia.org/core/v1/wikipedia/en/page/Earth

# Write requests
# Without OAuth or a csrf token, Core REST API returns a 400 error
{
  "actionModuleErrorCode": "missingparam",
  "messageTranslations": {
    "en": "The \"token\" parameter must be set."
  },
  "httpCode": 400,
  "httpReason": "Bad Request"
}
OAuth 2.0: App authorization (client-credentials workflow, two-legged)
  • For use by apps without a user authorization flow, effectively providing anonymous access for the user while including a client ID to support rate limiting.
  • Developer creates an OAuth 2.0 client with "refresh_token" and "client_credentials" as the grant types.
  • Client requires approval by an admin within 30 days to avoid expiration
# Get access token
POST https://meta.wikimedia.org/w/rest.php/oauth/access_token?grant_type=client_credentials&client_id=<client id>&client_secret=<client secret>

# Call API on behalf of app
GET https://api.wikimedia.org/core/v1/wikipedia/en/page/Earth
Authorization: Bearer <access-token>
OAuth 2.0: User authorization (authorization-code workflow, three-legged)
  • For use by apps that make requests on behalf of a user.
  • Developer creates an OAuth 2.0 client with "authorization_code", "refresh_token", and "client_credentials" as the grant types.
  • Client does not require manual approval to be authorized by the owner's account.
  • Client requires approval by an admin within 30 days to be authorized by other users and avoid expiration.
# Request authorization from user
https://meta.wikimedia.org/w/rest.php/oauth/authorize?client_id=<client id>&response_type=code

# Get access token
POST https://meta.wikimedia.org/w/rest.php/oauth/access_token?grant_type=authorization_code&code=<authorization code>&client_id=<client id>&client_secret=<client secret>&redirect_uri=<return URL>

# Call API on behalf of user
GET https://api.wikimedia.org/core/v1/wikipedia/en/page/Earth
Authorization: Bearer <access-token>

# Get refresh token
POST https://meta.wikimedia.org/w/rest.php/oauth/access_token?grant_type=refresh_token&refresh_token=<refresh token>
OAuth 2.0: Owner authorization (access-token-only workflow, single-legged)
  • For use by bot accounts.
  • Developer logs in to the API Portal with their local-wiki-approved bot account and creates an OAuth 2.0 client with the “This consumer is for use only by [Username]” option applied.
  • Client approved automatically upon creation.
# Call API on behalf of client owner
GET https://api.wikimedia.org/core/v1/wikipedia/en/page/Earth
Authorization: Bearer <access-token>
References

Event Timeline

Restricted Application added a subscriber: Aklapper. · View Herald TranscriptApr 8 2020, 10:46 PM
apaskulin triaged this task as Medium priority.Apr 8 2020, 10:47 PM

So, two things I don't love:

  • Using the CSRF tokens. I'd say, instead, no writes without OAuth cookie
  • Using meta.wikimedia.org for the OAuth authorization flow. Could it work through the portal instead?

Otherwise, seems about right.

Thanks, @eprodromou!

  • Using the CSRF tokens. I'd say, instead, no writes without OAuth cookie

Do you mean that the API Gateway would block anonymous write requests?

  • Using meta.wikimedia.org for the OAuth authorization flow. Could it work through the portal instead?

@BPirkle For the examples shown in the task description under "User-authorized requests", can you confirm whether the OAuth extension requires that they go through Meta Wiki or if they could go through the API Portal instead?

I tried a bunch of use cases against my local. This wiki is configured to allow anonymous users to read and create pages.

tl;dr: this largely works as expected, but there are some ways we could improve the implementation for developers

The commands I executed were:

The “Authorization Code” flow uses all those commands The “Client Credentials” flow skips the first one.

Reads:

FlowValid TokenNo TokenInvalid TokenExpired Token
Authorization Code200, results200, results403, rest-read-denied403, rest-read-denied
Client Credentials200, results200, results403, rest-read-denied403, rest-read-denied

Writes:

FlowValid TokenNo TokenInvalid TokenExpired Token
Authorization Code200, results400, missingparam403, rest-read-denied403, rest-read-denied
Client Credentials200, results400, missingparam403, rest-read-denied403, rest-read-denied

Notes:

  • If the client is permitted a Refresh Token, the response from the /oauth2/access_token call will include both the access token and the refresh token. On the command line, if you don’t look carefully, these looks like one big block of random characters and it is easy to cut and paste the whole thing without realizing you grabbed both tokens. Look for “refresh_token” in the big block and grab just the part before that. I could have saved myself a few hours if I’d realized that more quickly…
  • I could find no way to execute the “Authorization Code” flow for the anonymous user. This flow is intentionally (and presumably correctly) blocked at obtaining an authorization code by https://gerrit.wikimedia.org/g/mediawiki/extensions/OAuth/+/7dad004158d45e51c12baf5217febb4cd4dc10ed/src/Rest/Handler/Authorize.php#47
  • returning a 403 with error rest-read-denied on invalid token is supremely unhelpful. What actually happens in that the MWOAuthSessionProvider plumbing, in this case, creates an anonymous user with absolutely no rights, not even the rights normally associated with the anonymous user. The REST API endpoints then check for the “read” right via isReadAllowed, and that check fails. https://gerrit.wikimedia.org/g/mediawiki/core/+/7ac4ec51cc0608366b4873bf892770af4d6b7f20/includes/Rest/BasicAccess/BasicRequestAuthorizer.php#34 The mediawiki debug log contains “MediaWiki\Extensions\OAuth\MWOAuthSessionProvider::getAllowedUserRights: No provider metadata, returning no rights allowed”. This is slightly less unhelpful, but not much. There seems to be an opportunity for improvement here.
  • I need to make a code change to the OAuth Extension, files AccessTokenEntity.php and AuthorizationCodeAuthorization.php, to add a space before “is not usable”. The messages currently run two words together.
  • An anonymous user can’t do much via the API using the Authorization Code flow. Because they are unable to obtain an authorization code and generate an access token, they can’t even read. However, anonymous users can do read operations either via the Client Credentials flow, or if they effectively skip OAuth by not supplying an access token at all. This is inconvenient for a developer who wants to mirror the web interface and let both anonymous users and users with accounts do things they're normally accustomed to, but also supply a client id for rate limiting.

Suggested actions:

  • fix messages in OAuth Extension via trivial code change
  • revise the "rest-read-denied" behavior somehow. In my opinion, making an API request with an invalid/expired OAuth2 token should tell you that the token was invalid/expired. This may take some thought, because it involves disparate sections of the codebase.
  • consider the "no rights" behavior. Maybe there's a good reason for that, but =having the session provide two kinds of anonymous users was surprising to me

Thanks, @eprodromou!

  • Using the CSRF tokens. I'd say, instead, no writes without OAuth cookie

Do you mean that the API Gateway would block anonymous write requests?

That sounds like the responsibility of whatever implements the endpoints and not the gateway.

  • Using meta.wikimedia.org for the OAuth authorization flow. Could it work through the portal instead?

@BPirkle For the examples shown in the task description under "User-authorized requests", can you confirm whether the OAuth extension requires that they go through Meta Wiki or if they could go through the API Portal instead?

The OAuth extension on metawiki will be the only source of access tokens. I'm sure we could configure various path mappings if we chose, but the gateway just routes (and rate limits) requests - it doesn't fulfill them itself. So the request will end up up hitting meta. Once an application has an access token, they should be able to call whatever endpoints that like via the gateway without involving metawiki.

Part of the point of the API gateway is that we have one (1) uniform way to authenticate. I really don't want to use tokens.

For the meta wiki, it would be cool to use auth.wikimedia.org. "auth." is a common prefix for OAuth authentication servers. I can live with meta. for now if it's the best we can do.

apaskulin updated the task description. (Show Details)May 11 2020, 4:43 PM
apaskulin updated the task description. (Show Details)May 14 2020, 11:58 PM
apaskulin updated the task description. (Show Details)May 15 2020, 12:31 AM
apaskulin updated the task description. (Show Details)May 15 2020, 6:56 PM
apaskulin updated the task description. (Show Details)May 18 2020, 11:55 PM

Part of the point of the API gateway is that we have one (1) uniform way to authenticate. I really don't want to use tokens.

@eprodromou I think Bill was referring to access tokens obtained via an OAuth flow, so assuming OAuth 2.0 is the uniform authorization process we're aiming for, we should be ok here.

@BPirkle @eprodromou Can you both look this over and see if it looks correct? A few things to note:

  • Without OAuth or a csrf token, the Core REST API returns a 400 error and a message that the token is missing. Since we're not including csrf tokens in the docs, this could be confusing for developers.
  • With the existing OAuth approval process, the user authorization flow requires a manual approval step. Although this creates some friction for developers, I think it's ok for the MVP.

Part of the point of the API gateway is that we have one (1) uniform way to authenticate. I really don't want to use tokens.

@eprodromou I think Bill was referring to access tokens obtained via an OAuth flow, so assuming OAuth 2.0 is the uniform authorization process we're aiming for, we should be ok here.

Yep.

@BPirkle @eprodromou Can you both look this over and see if it looks correct? A few things to note:

  • Without OAuth or a csrf token, the Core REST API returns a 400 error and a message that the token is missing. Since we're not including csrf tokens in the docs, this could be confusing for developers.

Good point. I'm not sure what to do about it.

This happens in ActionModuleBasedHandler::throwHttpExceptionForActionModuleError: https://gerrit.wikimedia.org/g/mediawiki/core/+/aa6b910b7a98b3ec15e24e4b00d5e3cc3fbe719b/includes/Rest/Handler/ActionModuleBasedHandler.php#222

This handler is wrapping the Action API, and essentially just forwarding back the Action API error message. We probably don't want to change the existing and established Action API behavior. I suppose we could special case this in the Rest API handler, but that sort of thing is inherently brittle, and we'd have to be sure there weren't situations where we were making things worse. Shall I create a ticket for coming up with a more graceful solution? That'd give us a place to at least explore this in a dedicated conversation.

  • With the existing OAuth approval process, the user authorization flow requires a manual approval step. Although this creates some friction for developers, I think it's ok for the MVP.

I concur. I didn't notice any config variable that would allow this to be bypassed. I don't know of any technical reason that we couldn't add such a configuration option (now or later). But even if we added one, whether we'd actually use it on production or not is more a policy question than a technical one, and I have no insight into our history or thinking there. Presumably, any change we made would apply to clients created from either metawiki or the API Portal. Anyway, I agree that the current behavior is fine for the MVP.

apaskulin closed this task as Resolved.Jun 3 2020, 4:39 PM

Shall I create a ticket for coming up with a more graceful solution?

That sounds great. Thanks, @BPirkle!

Approved by Evan in our 1:1

apaskulin updated the task description. (Show Details)Jun 29 2020, 5:46 PM
apaskulin updated the task description. (Show Details)Jun 29 2020, 7:52 PM

I added a task to only allow read-only access without an OAuth 2.0 client ID here T256769.