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
MediaWiki version:
1.36

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 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
Get the names of the first three images from Wikimedia Commons.
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.

See also

edit