Manual:CORS/fr
Cette page détaille l'utilisation des requêtes CORS (cross-origin resource sharing) dans le code JavaScript pour communiquer entre les wikis de différents domaines.
L'API MediaWiki qui inclue l'API Action et l'API REST prend en charge les types de requêtes CORS suivants :
- les requêtes authentifiées qui utilisent les cookies, si
$wgCrossSiteAJAXdomains
est activé sur le wiki distant. C'est utile sur les sites Wikimedia, par exemple, pour permettre le téléversement d'images directement sur Commons depuis Wikipédia avec l'interface mobile.- comprend mais ne se limite pas aux requêtes authentifiées pour lesquelles un utilisateur connecté dans le wiki distant réalise une action au nom du wiki local
- les requêtes authentifiées utilisant OAuth.
- les requêtes anonymes, non authentifiées, utilisées principalement pour des actions plus limitées telles que la récupération des données disponibles publiquement.
Configuration
L'API Action et l'API REST imposent différentes contraintes pour le traitement des requêtes CORS.
Action API
Pour les requêtes à l'API Action, CORS est activé par défaut, mais l'URL de la requête doit contenir dans la partie texte de la requête, soit le paramètre origin
avec une valeur appropriée, soit le paramètre crossorigin
.
La raison est que les requêtes POST CORS sont préarrangées et que le paramètres origin
/crossorigin
doit être inclus dans la requête de préarrangement.
- Pour permettre les requêtes authentifiées en utilisant les cookies, utiliser
origin
et assurez-vous que la valeur correspond à l'une des valeurs initialisées dans$wgCrossSiteAJAXdomains
sur le wiki distant. Notez que la valeur du paramètre d'origine doit commencer par le protocole HTTPS (par exemplehttps://mediawiki.org
), même si$wgCrossSiteAJAXdomains
accepte des valeurs sans celui-ci. - Pour autoriser les requêtes authentifiées avec OAuth, utiliser
crossorigin
avec une valeur quelconque et une entête de requêteAuthorization
appropriée. Vous souhaiterez probablement utiliser OAuth 2 plutôt que OAuth 1.0a; voir OAuth/Pour les développeurs pour les détails. Depuis MediaWiki 1.44 • change 1118583 - Pour autoriser les requêtes anonymes de n'importe où, initialisez le paramètre
origin
de la chaîne de requête avec un astérisque*
.
API REST
Pour permettre les requêtes authentifiées, vous avez deux possibilités :
- utiliser l'extension OAuth (approche recommandée). L'API REST a été conçue pour être utilisée avec l'extension OAuth pour l'authentification et les autorisations des utilisateurs.
- mettre $wgRestAllowCrossOriginCookieAuth à
true
pour que quelque soit l'origine spécifiée dans $wgCrossSiteAJAXdomains elle puisse émettre les cookies de session pour l'autorisation dans l'API REST.
Pour permettre les requêtes anonymes à l'API REST, $wgAllowCrossOrigin doit être initialisé avec true
sur le wiki distant.
Ceci initialisera Access-Control-Allow-Origin
dans l'entête avec *
.
A la différence de l'API Action, l'API REST n'utilise pas le paramètre origine de l'URL de la requête.
Méthode JavaScript
Utiliser mediawiki.ForeignApi
Le ResourceLoader de MediaWiki offre le module mediawiki.ForeignApi
qui est une extension de mediawiki.api
et qui gère automatiquement les détails pôur vous. Deux constructeurs sont proposés pour activer CORS :
- Pour les demandes à l'API Action, utiliser
mw.ForeignApi
. (introduit dans 1.26)
- Pour les requêtes à l'API REST, utiliser
mw.ForeignRest
. (introduit dans 1.36)
Voir les exemples ci-dessous.
Pour utiliser mw.ForeignApi
ou mw.ForeignRest
, les extensions doivent définir mediawiki.ForeignApi
en tant que dépendance du module ResourceLoader dans extension.json.
Si vous utilisez mw.ForeignApi()
avec une requête POST (.post()
), alors origin=*
sera inclus automatiquement.
Si vous devez utiliser mw.ForeignApi() avec une requête GET (.get()
), assurez-vous (si nécessaire) que origin=*
est ajouté directement dans l'URL (et non à la chaîne de la requête).
Si c'est nécessaire pour l'action demandée (que l'utilisateur du wiki distant soit connecté), passez le paramètre assert: 'user'
à get()
/post()
.
Pour confirmer que l'utilisateur du wiki distant a un nom d'utilisateur particulier, passer le paramètre assertuser
avec le nom d'utilisateur souhaité.
Utiliser Fetch
Si la demande GET peut être faite de manière anonyme, vous pouvez également utiliser Fetch (le remplacement moderne et basé sur la promesse XMLHttpRequest).
Utiliser jQuery.ajax
Si vous ne voulez pas utiliser mediawiki.api pour une raison quelconque, ou si vous êtes intéressé par connaître son fonctionnement à un niveau plus profond, voici comment implémenter la même fonctionnalité en utilisant directement les fonctions AJAX jQuery.
Vous pourriez même utiliser directement XMLHttpRequest
.
Les exemples sont donnés ci-dessous.
Si vous voulez que le navigateur utilise les cookies qu'il pourrait avoir concernant le domaine, afin de garder l'utilisateur actuel connecté, vous devez aussi initialiser le champ withCredentials
de XMLHttpRequest
à true
.
Exemples
Dans les exemples ci-dessous, nous supposons que le wiki local à partir duquel la requête a été émise est www.mediawiki.org
, et que le wiki distant - celui vers lequel la requête est dirigée - est en.wikipedia.org
.
Utiliser mw.ForeignApi
Requêtes authentifiées
Un exemple qui vérifie si l'utilisateur est connecté sur le wiki distant :
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})` );
Un exemple de base d'API d'écriture. Nous demandons un jeton csrf
et l'utilisons pour initialiser une préférence utilisateur persistante qu'un gadget pourra utiliser après cela :
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!'
} );
Le même exemple peut être réécrit plus succintement en utilisant quelques méthodes de mediawiki.api helper, qui sont disponibles également pour ForeignApi :
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!'
} );
Requêtes anonymes
Si le wiki cible n'accepte pas les requêtes inter-origines, ou si vous n'avez pas besoin de réaliser des actions d'écriture ou de lire des informations restreintes et que vous voulez éviter la surcharge, vous pouvez alors initialiser l'option anonymous
dans le constructeur mediawiki.ForeignApi
:
await mw.loader.using( 'mediawiki.ForeignApi' )
const api = new mw.ForeignApi( 'https://en.wikipedia.org/w/api.php', { anonymous: true } );
...
Utiliser mw.ForeignRest
Exemple utilisant l'API REST pour obtenir le code HTML de la page principale :
var api = new mw.ForeignRest( 'https://commons.wikimedia.org/w/rest.php/v1' );
await api.get( '/page/Main_Page/html' );
Un exemple qui interroge les pages de Wikimedia Commons dont les noms de page commencent par "Test" (et qui enregistre ensuite les résultats dans le navigateur) :
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 );
} );
Utiliser Fetch
Get
var apiEndpoint = "https://commons.wikimedia.org/w/api.php";
var params = "action=query&list=allimages&ailimit=3&format=json";
/**
* Envoyer la requête pour obtenir les images
*/
fetch( apiEndpoint + "?" + params + "&origin=*" )
.then(function(response){
return response.json();
})
.then(function(response) {
var allimages = response.query.allimages; // Traiter la sortie pour obtenir le nom des images
Object.keys(allimages).forEach(function(key) {
console.log(allimages[key].name);
});
});
Post
var apiEndpoint = 'https://test.wikipedia.org/w/api.php';
var params = {
action: 'edit',
title: 'Wikipedia:Sandbox',
text: 'Hello World',
summary: 'Hello World',
format: 'json',
formatversion: '2',
crossorigin: ''
}
// Remplacer par le jeton actuel OAuth 2
var oauthToken = "OAuthAccessToken";
/**
* Obtenir un jeton CSRF
*/
var tokenQuery = {
action: 'query',
meta: 'tokens',
format: 'json',
formatversion: '2',
crossorigin: ''
};
var queryURL = new URL(apiEndpoint);
queryURL.search = new URLSearchParams(tokenQuery);
fetch(queryURL, {method: 'GET', headers: {'Authorization': 'Bearer ' + oauthToken}})
.then(function(response){return response.text()})
.then(function(data){
try {data=JSON.parse(data);} catch (e) {console.error(e);}
params.token = data?.query?.tokens?.csrftoken;
if (params.token) {
/**
* Modification a posteriori.
* Action API requires data be posted as application/x-www-form-urlencoded (URLSearchParams) or multipart/form-data, rather than application/json (T212988)
*/
var postBody = new URLSearchParams();
queryURL = new URL(apiEndpoint);
Object.keys(params).forEach( key => {
if ( key == 'action' || key == 'origin' || key == 'crossorigin' ) {
queryURL.searchParams.append(key, params[key]);
} else {
postBody.append(key, params[key]);
}
});
fetch(queryURL, {method: 'POST', headers: {'Authorization': 'Bearer ' + oauthToken}, body: postBody})
.then(function(response){return response.text()})
.then(function(data){
try {data=JSON.parse(data);} catch (e) {console.error(e);}
var result = data?.edit?.result;
if (result) {
console.log(result);
} else {
console.error("Error posting edit!");
}
});
} else {
console.error("Error retrieving CSRF token!");
}
});
Utiliser jQuery.ajax
Un exemple qui vérifie si l'utilisateur est connecté sur le wiki distant :
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})` );
Un exemple de base d'API d'écriture :
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'
} );
Exemples d'authentification OAuth
Version de MediaWiki : | 1.44 Gerrit change 1118583 |
Les deux exemples externes suivants montrent comment utiliser OAuth 2 pour faire des requêtes CORS authentifiées :
- Application Vuetify avec connexion OAuth Wikimedia, en se concentrant sur les détails du flux d'autorisation, avec des liens vers le code exécutablee
- exemple m3api du webbapp-clientside-vite-guestbook, une application d'exemple exécutable pour un ensemble de bibliothèques JavaScript que vous pouvez utiliser
Extensions au mécanisme
CentralAuth
Version de MediaWiki : | ≥ 1.26 |
CentralAuth permet à votre code d'authentifier sur le wiki distant l'utilisateur actuellement connecté sur le wiki local en utilisant un centralauthtoken. Ceci garantit que le même compte associé sera utilisé pour les actions sur les deux wikis, au contraire d'un CORS régulier (qui nécessite que l'utilisateur se soit auparavent connecté au wiki distant).
Si à la fois le wiki local et le wiki distant ont installé CentralAuth, le mécanisme mediawiki.ForeignApi est étendu de la mème manière pour gérer cela à votre place. Pour l'implémenter vous-même, voir centralauthtoken pour les instructions sur la manière d'acquérir un jeton (du wiki local) et le passer à une requête (au wiki distant).
Alternatives à CORS
Pour les requêtes anonymes vous pouvez utiliser le format JSONP à la place. Ceci est plus simple mais un peu moins sécurisé (il accède et exécute du code JavaScript arbitraire sur le wiki si bien qu'un assaillant qui prendrait la main sur le site MediaWiki aurait un vecteur XSS sur le site distant).