User:Chlod/OAuth

OAuth/For Developers is a little too summarized for anyone who wants an in-depth explanation of how OAuth works in the context of MediaWiki. This document attempts to break it down and explain most details without being too complicated.

Before starting
Do note that a lot of libraries already exist to support standard OAuth authentication flows. There's no need to reinvent the wheel unless you really have to, such as if you're on space or bandwidth constraints (such as in an on-wiki userscript). If you can, use those libraries, as they likely have had much more testing and quality checking prior to being released.

Glossary
This is a short glossary of terms for newer developers. Note that some of the explanations here are massively oversimplified. Cryptography is a very complex field, so some level of simplification is required to make concepts easier to understand.


 * Consumer refers to any application which uses information using OAuth.
 * Nice URL refers to a formatted URL, such as https://en.wikipedia.org/wiki/Main_Page.
 * Non-nice URL refers to an unformatted URL, such as https://en.wikipedia.org/w/index.php?title=Main_Page.
 * RSA refers to Rivest–Shamir–Adleman encryption, a method of encryption which requires the use of a public key (one that can be shared with anyone and can only encrypt data) and a private key (one that you must keep secret and can be used to both encrypt and decrypt data).
 * HMAC refers to hash-based message authentication code, a method of cryptography which relies on a pre-shared secret instead of keys. This (or RSA) is used to ensure that the OAuth 1.0a authorization request came from your application itself.

OAuth 1.0a
OAuth 1.0a requires you to provide OAuth information using the Authorization HTTP header. As a reference, we're going to use the English Wikipedia API for OAuth authorization. We'll assume that we're working with a non-owner-only application with no provided public RSA key on consumer registration.

Three-legged OAuth
Three-legged OAuth is used whenever creating a non-owner-only application. It's called "three-legged" since it consists of three steps.

Step 1: Initiate
To initiate authorization, make a GET request to Special:OAuth/initiate using the non-nice URL (for example, https://en.wikipedia.org/w/index.php?title=Special:OAuth/initiate) with an OAuth authorization header. You should use an OAuth library to do these steps automatically, but a low-level representation is provided below for those who prefer to work with plain JavaScript or curl.

JavaScript
You can use the oauth-1.0a npm package for automatically signing your OAuth requests. This eases the process of making signed requests.

Low-level
The OAuth authorization header looks like the following: OAuth oauth_consumer_key="XXXXX",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1640138239",oauth_nonce="44938yXv2GT",oauth_callback="oob",oauth_signature="..." It consists of the following parts:


 * – a mandatory string at the start of the header, indicating that this is an OAuth authorization header.
 * – your application's consumer key. This is shown only once when registering your application on Special:OAuthConsumerRegistration/propose.
 * – indicates the signature method used to generate the OAuth signature (provided in a later field). Since we set the application up without providing a public RSA key, this will be set to, the automatic method selected.
 * – the current UNIX timestamp as of the time the message was made and signed.
 * – a randomly-generated string of characters which prevent you from accidentally re-initiating an OAuth authorization (such as when a user retries a failed web request). You should always change this whenever making a new request!
 * – the callback URL to send the user to after authorization. If during consumer registration, this was set to a constant URL (and not as a prefix), the value must be set to "oob". Otherwise, it should be set to the callback URL you wish to send the user to after authorizing.
 * – an HMAC-SHA1 hash of all signed values. We'll get into that below.

The HMAC-SHA1 hash is a hashed representation of a string called the "signature base string". This string contains information about the request you're making and the OAuth consumer information as well. If your hash does not compute properly (possibly due to an incorrectly-computed hash), your authorization request will not begin.

The signature base string is made with the HTTP request type, the HTTP URL, and the "parameter string". The parameter string is a string which contains all values to be signed, including query parameters and all OAuth fields. The following JavaScript function generates a parameter string. After creating the parameter string, you can then create the signature base string by combining the HTTP method, the (percent-encoded) URL, and the percent-encode parameter string. When you're done, the signature base string should look like this: GET&https%3A%2F%2Fen.wikipedia.org%2Fw%2Findex.php%3Ftitle%3DSpecial%3AOAuth%2Finitiate&oauth_consumer_key%3DXXXXX%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1640138239%26oauth_nonce%3D44938yXv2GT%26oauth_callback%3Doob and if your consumer key was, the HMAC-SHA1 signature should be.

Step 2: Authorize
Step 1 should have given you a request token and secret in the  and   fields, respectively. From here, you need to redirect the user to a Special:OAuth/authorize, with the  query parameter set as your consumer key and   set as the token provided in the previous request. The user should see a confirmation dialog showing them what data is being requested and some basic information about the app.

If they choose not to give access, they are informed by Special:OAuth. If they gave access, you can proceed to Step 3.

Step 3: Getting the token
The user will be redirect to the callback URL provided with an  query parameter. You now need to sign another request, this time to https://en.wikipedia.org/w/index.php?title=Special:OAuth/token, with the request token and request secret set as your OAuth access token and token secret, and the  parameter.

MediaWiki will respond with the new access token and secret that you need for whatever requests you'll be making next, such as those with the Action API. All of those requests must be signed much like how Step 3 signs the request to get the OAuth token.

JavaScript
This picks up from the code used in Step 1.

OAuth 2.0
OAuth 2.0 is the preferred method of employing OAuth with a MediaWiki instance, as it requires less steps and simplifies the flow for developers. The concept of an access secret is removed, and only the access token is required for authenticating a user. OAuth 2 tokens, however, have a very short expiry time and need to be refreshed with a "refresh token" when they expire. This, however, gets rid of the need to reauthorize every once-in-a-while for users.

OAuth 2.0 also introduces the concept of confidential and public (non-confidential) consumers. Confidential consumers can keep their client secret a secret, however public consumers cannot. Public consumers are usually found in embedded systems or mobile applications, where the system where the secret is located can be cracked open and stolen. This section will discuss confidential consumers, see for the section on public consumers.

The MediaWiki OAuth 2.0 flow runs on the REST API instead of on Special pages. Unlike other REST API endpoints, it is not prefixed with a version identifier. Like with OAuth 1.0a, we'll use the English Wikipedia as an example for the authorization flow.

Step 1: Authorization
Send the user to https://en.wikipedia.org/w/rest.php/oauth2/authorize with the following query parameters:


 * set to . This tells MediaWiki you're looking to get an authorization code.
 * set to your application key (i.e. consumer key).
 * (optional but recommended)  set to a random string. When you receive your code in Step 2, you need to check if the state matches, or else you might open a user up to a CSRF attack. Consider the birthday problem when picking the length of the random string.
 * (optional)  set to the URL to redirect to (depending on how you set up the callback URL when registering the consumer).

The user, should they choose to authorize the application, will then be sent to your callback URL for step 2.

Step 2: Get the access token
When the user is redirected back to your application, you will be provided a  parameter and the   (if provided) that was provided in Step 1.

First and foremost, check if the state matches with the state you stored. If it does, then this is likely a genuine request from the user.

The provided code is called the authorization code, and is used to get the access token and refresh token for a user. To trade in your authorization code for an access token, you'll need to make another REST API request, this time a POST request to https://en.wikipedia.org/w/rest.php/oauth2/access_token, containing the following body parameters (as, not as query parameters).


 * set to your application key.
 * set to your application secret.
 * set to . This tells MediaWiki you're asking for an access token with an authorization code.
 * set to your authorization code.
 * (optional)  set to the redirect URI you used in Step 1. They must match or else MediaWiki will complain.

You should receive a JSON response with the access token (as ) and refresh token (as  ). You'll also receive the date on which the access token will expire (as, provided as a UNIX timestamp), after which you'll need to request a new access token using the refresh token.

Step 2.5: Refreshing the token
Although this isn't really part of the authorization flow, it still needs to be part of your application if you plan to reuse the tokens at some point. Since the access token will expire at some point, you'll need to refresh the token to get a new access token and refresh token.

To do this, do the same thing as in Step 2 except set the  to   instead and set   to your refresh token. Obviously, you will no longer be providing a  in your request.

You should receive a response identical to the one used in Step 2.

Step 3: Making requests
To make requests on the user's behalf, send requests to the wiki's Action API (i.e. ) with the access token in the Bearer slot.