5

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)
loretoparisi
  • 15,724
  • 11
  • 102
  • 146

2 Answers2

4

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.

nirinsanity
  • 141
  • 1
  • 5
3

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
          });
lonesomeday
  • 233,373
  • 50
  • 316
  • 318
  • so, this was an attempt I did, the server complains that `"info": "The following parameters were found in the query string, but must be in the POST body: lgpassword, lgtoken."`, so I have moved in the body only as in the code above... – loretoparisi Mar 19 '18 at 16:54
  • @loretoparisi I can't debug your code psychically! What is your `RequestPostP` function doing with the request body parameters? If they are JSON-encoded, that is wrong... – lonesomeday Mar 19 '18 at 16:57
  • okay thanks, this could be one of the issue! I have disabled json encoding of the body (as it was) and passed the `lgpassword=xxxxxx&lgtoken=xxxxxx`. The error code is still `result.NeedToken` and this could explain the error since a `lgtoken` is expected in the body, while it is not for some reason. I have also added a `'Content-Type': 'application/x-www-form-urlencoded'` to be sure I'm posting a raw url encoded body. – loretoparisi Mar 19 '18 at 17:03
  • Are you URL-encoding it then? If you are leaving it to the library to convert it from an object to a string, you'll probably end up with `[object Object]`. – lonesomeday Mar 19 '18 at 17:12
  • So I'm using `qs.stringify` as for here: https://github.com/loretoparisi/audionamix.js/blob/master/lib/api.js#L151 – loretoparisi Mar 19 '18 at 17:13
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/167108/discussion-between-lonesomeday-and-loretoparisi). – lonesomeday Mar 19 '18 at 17:29