Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Login to MediaWiki (Wikipedia) API in Node.js

I'm trying the Wikipedia client login flow depicted in the API:Login docs, but something wrong happens:

1) I correctly get a token raised with the HTTP GET https://en.wikipedia.org/w/api.php?action=query&meta=tokens&type=login&format=json

and I get a valid logintoken string.

2.1) I then try the clientlogin like:

HTTP POST /w/api.php?action=clientlogin&format=json&lgname=xxxx&lgtoken=xxxx%2B%5C

and the POST BODY was

{
    "lgpassword" : "xxxxx",
    "lgtoken" : "xxxxx"
}

But I get an error:

{
  "error": {
    "code": "notoken",
    "info": "The \"token\" parameter must be set."
 },
  "servedby": "mw1228"
}

If I try to change lgtoken to token I get the same result.

2.2) I have then tried the old method i.e. action=login and passing the body, but it does not work, since it gives me back another login token: HTTP POST https://en.wikipedia.org/w/api.php?action=login&format=json&lgname=xxxx

and the same POST BODY

I then get

{
  "warnings": {}
  },
  "login": {
    "result": "NeedToken",
    "token": "xxxxx+\\"

}

where the docs here states that

NeedToken if the lgtoken parameter was not provided or no session was active (e.g. your cookie handling is broken).

but I have passed the lgtoken in the json body as showed. I'm using Node.js and the built-in http module, that is supposed to pass and keep session Cookies in the right way (with other api it works ok).

I have found a similar issue on a the LrMediaWiki client here.

[UPDATE] This is my current implementation:

  Wikipedia.prototype.loginUser = function (username, password) {
      var self = this;
      return new Promise((resolve, reject) => {

        var cookies = self.cookies({});
        var headers = {
          'Cookie': cookies.join(';'),
          'Accept': '*/*',
          'User-Agent': self.browser.userAgent()
        };
        // fetch login token
        self.api.RequestGetP('/w/api.php', headers, {
          action: 'query',
          meta: 'tokens',
          type: 'login',
          format: 'json'
        })
          .then(response => { // success
            if (response.query && response.query.tokens && response.query.tokens['logintoken']) {
              self.login.logintoken = response.query.tokens['logintoken'];
              self.logger.info("Wikipedia.login token:%s", self.login);
              return self.api.RequestPostP('/w/api.php', headers, {
                action: 'login',
                format: 'json',
                lgname: username
              },
                {
                  lgpassword: password,
                  lgtoken: self.login.logintoken
                });
            } else {
              var error = new Error('no logintoken');
              return reject(error);
            }
          })
          .then(response => { // success
            return resolve(response);
          })
          .catch(error => { // error
            self.logger.error("Wikipedia.login error%s\n%@", error.message, error.stack);
            return reject(error);
          });
      });
    }//loginUser

where this.api is a simple wrapper of the Node.js http, the source code is available here and the api signatures are like:

Promise:API.RequestGetP(url,headers,querystring)
Promise:API.RequestPostP(url,headers,querystring,body)
like image 344
loretoparisi Avatar asked Mar 19 '18 15:03

loretoparisi


People also ask

What is MediaWiki API?

The MediaWiki Action API is a RESTful web service that allows users to perform certain wiki-actions like page creation, authentication, parsing, searching, etc. API:Main page is a good starting point for understanding the API. Your program sends requests to the API to get access to wiki features.

Does Wikipedia have an open API?

Because Wikipedia is built using MediaWiki, which in turn supports an API, Wikipedia does as well. This provides developers code-level access to the entire Wikipedia reference. The goal of this API is to provide direct, high-level access to the data contained in the MediaWiki databases.


2 Answers

I think from what you are saying you have lgtoken and lgname in the URL you are using, and then lgpassword and lgtoken (again!) in a JSON-encoded POST body.

This is not how the Mediawiki API works.

You submit it all as POST parameters. JSON is never involved, except when you ask for the result to come back in that format. I can't help you fix your code as you don't provide it, but that's what you need to do. (If you edit your question with your code, I'll do my best to help you.)

After seeing your code, I'll presume (without knowing the detail of your code) that you want something like this:

          return self.api.RequestPostP('/w/api.php', headers, {
            action: 'login',
            format: 'json',
            lgname: username,
            lgpassword: password,
            lgtoken: self.login.logintoken
          });
like image 95
lonesomeday Avatar answered Oct 25 '22 00:10

lonesomeday


If the currently accepted answer isn't working for someone, the following method will definitely work. I've used the axios library to send requests. Any library can be used but the key lies in formatting the body and headers correctly.

let url = "https://test.wikipedia.org/w/api.php";

let params = {
    action: "query",
    meta: "tokens",
    type: "login",
    format: "json"
};

axios.get(url, { params: params }).then(resp => {
    let loginToken = resp.data.query.tokens.logintoken
    let cookie = resp.headers["set-cookie"].join(';');

    let body = {
        action: 'login',
        lgname: 'user_name',
        lgpassword: 'password',
        lgtoken: loginToken,
        format: 'json'
    }
    let bodyData = new URLSearchParams(body).toString();

    axios.post(url, bodyData, {
        headers: {
            Cookie: cookie,
        }
    }).then(resp => {
        // You're now logged in!

        // You'll have to add the following cookie in the headers again for any further requests that you might make
        let cookie = resp.headers["set-cookie"].join(';')

        console.log(resp.data)
    })
})

And you should be seeing a response like

{
    login: { result: 'Success', lguserid: 0000000, lgusername: 'Username' }
}

The second post request was where I got stuck for several hours, trying to figure out what was wrong. You need to send the data in an encoded form by using an API like URLSearchParams, or by just typing up the body as a string manually yourself.

like image 4
nirinsanity Avatar answered Oct 25 '22 00:10

nirinsanity