Manual:CORS
This page details how to use CORS (cross-origin resource sharing) requests in your JavaScript code to communicate between wikis on different domains.
The MediaWiki API, which includes the Action API and REST API, supports two kinds of CORS requests:
- authenticated requests if
$wgCrossSiteAJAXdomains
is enabled on the remote wiki. This is used on Wikimedia sites to do things like allowing image uploads directly to Commons from Wikipedia sites on the mobile interface.- includes but is not limited to authenticated requests in which a logged in user at the remote wiki performs an action on behalf of the local wiki
- anonymous, non-authenticated requests, used mainly for more limited actions such as fetching publicly available data.
Configuration
[edit]The Action API and REST API come with different requirements for handling CORS requests.
Action API
[edit]For requests to the Action API, CORS is enabled by default, but the request URL must include the origin
parameter, with an appropriate value, in its query string.
The reason is that POST CORS requests are preflighted and the origin parameter must be included in the preflight request.
- To allow authenticated requests, make sure the value corresponds to one of the values set in
$wgCrossSiteAJAXdomains
on the foreign wiki. Note that the value of the origin parameter must begin with the HTTPS protocol (e.g.https://mediawiki.org
), even if$wgCrossSiteAJAXdomains
accepts values without it. - To allow anonymous requests from anywhere, set the
origin
query string parameter to*
, an asterisk.
REST API
[edit]To allow authenticated requests with session cookies, you can do either of two things:
- use the OAuth extension (recommended approach). The REST API was designed to be used with the OAuth extension for user authentication and authorization.
- set $wgRestAllowCrossOriginCookieAuth to true so that any origin specified in $wgCrossSiteAJAXdomains may send session cookies for authorization in the REST API.
To allow anonymous requests to the REST API, $wgAllowCrossOrigin
must be set to true
on the remote wiki. This will set Access-Control-Allow-Origin in the header to *
.
Unlike the Action API, the REST API does not come with an origin parameter in the request URL.
JavaScript methods
[edit]Using mediawiki.ForeignApi
[edit]MediaWiki's ResourceLoader offers the mediawiki.ForeignApi
module, which is an extension of mediawiki.api
and automatically handles the details for you. It offers two constructors for enabling CORS:
MediaWiki version: | ≥ 1.26 |
- For requests to the Action API, use
mw.ForeignApi
.
MediaWiki version: | ≥ 1.36 |
- For requests to the REST API, use
mw.ForeignRest
.
Examples are given below. To use mw.ForeignApi
or mw.ForeignRest
, extensions should set mediawiki.ForeignApi
as a ResourceLoader module dependency in extension.json.
If you use mw.ForeignApi()
with a POST request (.post()
), then origin=*
will be included automatically. If you need to use mw.ForeignApi() with a GET request (.get()
), make sure that origin=*
, if required, is appended directly to the URL (not to the query string).
If it is necessary for the requested action that the user at the foreign wiki is logged in, pass the assert: 'user'
parameter to get()
/post()
. To assert that the user at the foreign wiki has a specific username, pass the assertuser
parameter with the desired username.
Using Fetch
[edit]If the GET request can be made anonymously, you can also use Fetch (the modern, Promise-based replacement for XMLHttpRequest).
Using jQuery.ajax
[edit]If you do not wish to use mediawiki.api for whatever reason, or if you're curious how this works at a lower level, you can implement the same functionality using plain jQuery AJAX functionality. You could even use plain XMLHttpRequest
.
Examples are given below.
If the current user should remain logged in and so you need the browser to use cookies it might have for the domain, you also need to set the withCredentials
field of XMLHttpRequest
to true
.
Examples
[edit]In the examples below, we assume that the local wiki from which the requests originate is www.mediawiki.org
, and that the foreign wiki which the requests target is en.wikipedia.org
.
Using mw.ForeignApi
[edit]Authenticated requests
[edit]An example that checks whether the user is logged in on the foreign wiki:
await mw.loader.using( 'mediawiki.ForeignApi' )
const api = new mw.ForeignApi( 'https://en.wikipedia.org/w/api.php' );
const data = await api.get( { action: 'query', meta: 'userinfo' } );
alert( `Foreign user ${data.query.userinfo.name} (ID ${data.query.userinfo.id})` );
A basic write API example. We're acquiring a csrf
token and using it to set a persistent custom user preference that a gadget might use afterwards:
await mw.loader.using( 'mediawiki.ForeignApi' );
const api = new mw.ForeignApi( 'https://en.wikipedia.org/w/api.php' );
const data = await api.get( { action: 'query', meta: 'tokens' } );
await api.post( {
action: 'options',
token: data.query.tokens.csrftoken,
optionname: 'userjs-test',
optionvalue: 'Hello world!'
} );
The same example can be rewritten more succinctly using some mediawiki.api helper methods, which are available for ForeignApi too:
await mw.loader.using( 'mediawiki.ForeignApi' );
const api = new mw.ForeignApi( 'https://en.wikipedia.org/w/api.php' );
await api.postWithToken( 'options', {
action: 'options',
optionname: 'userjs-test',
optionvalue: 'Hello world!'
} );
Anonymous requests
[edit]If the target wiki does not accept cross-origin requests, or if you don't need to perform write actions or read restricted information and want to avoid the overhead, you may set the anonymous
option of the mediawiki.ForeignApi
constructor:
await mw.loader.using( 'mediawiki.ForeignApi' )
const api = new mw.ForeignApi( 'https://en.wikipedia.org/w/api.php', { anonymous: true } );
...
Using mw.ForeignRest
[edit]An example that uses the REST API to get the html of the main page:
var api = new mw.ForeignRest( 'https://commons.wikimedia.org/w/rest.php/v1' );
await api.get( '/page/Main_Page/html' );
An example that queries pages whose page names start with "Test" from Wikimedia Commons (and then logs the results in the browser):
var value = "Test";
var actionApi = new mw.ForeignApi( 'https://commons.wikimedia.org/w/api.php' );
const api = new mw.ForeignRest(
'https://commons.wikimedia.org/w/rest.php/v1',
actionApi,
{ anonymous: true }
);
api.get( '/search/title', {
limit: '10',
q: `${ encodeURIComponent( value ) }`,
origin: '*'
} )
.done( function ( data ) {
console.log( data );
} );
Using Fetch
[edit]var apiEndpoint = "https://commons.wikimedia.org/w/api.php";
var params = "action=query&list=allimages&ailimit=3&format=json";
/**
* Send the request to get the images
*/
fetch( apiEndpoint + "?" + params + "&origin=*" )
.then(function(response){
return response.json();
})
.then(function(response) {
var allimages = response.query.allimages; // Process the output to get the image names
Object.keys(allimages).forEach(function(key) {
console.log(allimages[key].name);
});
});
Using jQuery.ajax
[edit]An example that checks whether the user is logged in on the foreign wiki:
const { query } = await $.ajax( {
url: 'https://en.wikipedia.org/w/api.php',
data: {
action: 'query',
meta: 'userinfo',
format: 'json',
origin: 'https://www.mediawiki.org'
},
xhrFields: {
withCredentials: true
},
dataType: 'json'
} );
alert( `Foreign user ${query.userinfo.name} (ID ${query.userinfo.id})` );
A basic write API example:
const { query } = await $.ajax( {
url: 'https://en.wikipedia.org/w/api.php',
data: {
action: 'query',
meta: 'tokens',
format: 'json',
origin: 'https://www.mediawiki.org'
},
xhrFields: {
withCredentials: true
},
dataType: 'json'
} );
await $.ajax( {
url: 'https://en.wikipedia.org/w/api.php?origin=https://www.mediawiki.org',
method: 'POST',
data: {
action: 'options',
format: 'json',
token: query.tokens.csrftoken,
optionname: 'userjs-test',
optionvalue: 'Hello world!'
},
xhrFields: {
withCredentials: true
},
dataType: 'json'
} );
Extensions to the mechanism
[edit]CentralAuth
[edit]MediaWiki version: | ≥ 1.26 |
CentralAuth allows your code to authenticate on the foreign wiki as the user currently logged in on the local wiki using a centralauthtoken. This guarantees that the same associated account will be used for actions on both wikis, unlike regular CORS (which requires the user to have previously logged in to the foreign wiki).
If both the local and foreign wiki have CentralAuth installed, the mediawiki.ForeignApi mechanism is seamlessly extended to handle this for you. If you're implementing it yourself, see centralauthtoken for instructions on how to acquire a token (from the local wiki) and pass it to a request (to the foreign wiki).
Alternatives to CORS
[edit]For anonymous requests you can use the JSONP format instead. This is simpler but slightly less secure because it fetches and executes arbitrary JavaScript code from the wiki so an attacker who took over the MediaWiki site has an XSS vector against the remote site.