CXuesong / WikiClientLibrary

/*🌻*/ Wiki Client Library is an asynchronous MediaWiki API client library targeting modern .NET platforms
https://github.com/CXuesong/WikiClientLibrary/wiki
Apache License 2.0
82 stars 16 forks source link

Occasional "badtoken:Invalid CSRF token" error #21

Closed zstadler closed 7 years ago

zstadler commented 7 years ago

https://github.com/IsraelHikingMap/Site/issues/481 is about occasional failure to upload pictures to Wikimedia using WikiClientLibrary.

Many uploads succeeded, but from time to time, the there is a "badtoken:Invalid CSRF token" exception. For example:

2017-09-22 21:33:37.7688 | 13|ERROR| soft.AspNetCore.Server.Kestrel | Connection id "0HL81JJA3C9J5": An unhandled exception was thrown by the application. WikiClientLibrary.OperationFailedException: badtoken:Invalid CSRF token.
   at WikiClientLibrary.Client.WikiClient.CheckErrors(JToken jresponse)
   at WikiClientLibrary.Client.WikiClient.<SendAsync>d__0.MoveNext()

What is this error? How can it be avoided?

CXuesong commented 7 years ago

In WCL the tokens fetched, except for login, are cached until you login/logout the WikiSite. This is helpful for reducing unnecessary network traffics. Though the tokens are changed from time to time, the previously retrieved tokens are usually still valid; at least it works well on WP.

However, for Wikimedia Commons, the situation seems a little bit different. I am still investigating this. During this time, you may force refreshing the edit token each time before uploading the picture (i.e. await site.GetTokenAsync("edit", true)). I believe the error should go away.

The following unit test demonstrates that the tokens are changed almost every second

    public class Playground : UnitTestsBase
    {

        /// <inheritdoc />
        public Playground(ITestOutputHelper output) : base(output)
        {
        }

        [Fact]
        public async Task Test1()
        {
            var client = new WikiClient();
            var site = await WikiSite.CreateAsync(client, "https://commons.wikimedia.org/w/api.php");
            Output.WriteLine(await site.GetTokenAsync("edit", true));
            Output.WriteLine(await site.GetTokenAsync("edit", true));
            Output.WriteLine(await site.GetTokenAsync("edit", true));
            Output.WriteLine(await site.GetTokenAsync("edit", true));
            await site.LoginAsync("xxxx", "xxxx");
            Output.WriteLine(await site.GetTokenAsync("edit", true));
            Output.WriteLine(await site.GetTokenAsync("edit", true));
            Output.WriteLine(await site.GetTokenAsync("edit", true));
            Output.WriteLine(await site.GetTokenAsync("edit", true));
            await site.LogoutAsync();
            Output.WriteLine(await site.GetTokenAsync("edit"));
        }

    }

The output is

+\
+\
+\
+\
193436c60e699225cd9bd734da8d603959c6231e+\
a4dda18cfdaed80fb38a5212cc6f7c6559c6231f+\
a4dda18cfdaed80fb38a5212cc6f7c6559c6231f+\
093ec676ffaa14d8014c9a9dd453ade259c62320+\
+\

cc @HarelM

CXuesong commented 7 years ago

I tried to upload different images on Wikimedia commons beta site but met no badtoken error, so I assume the token invalidation happens sporadically. Considering pywikibot has retry-once logic upon this error, I will implement one in WCL.

CXuesong commented 7 years ago

Released v0.6-int4. It supports re-fetching the tokens on badtoken now.

This release contains a lot of changes so please take a look at the release notes before upgrading.

CXuesong commented 7 years ago

By the way, you may set WikiClient.LoggingFactory to see the logs that might be useful. After setting the property, by default all the WikiSite, WikiPage, etc. constructed from this WikiClient will inherit the same logging factory and will emit the logs.

WikiSite will log the badtoken error and the retry attempt.

CXuesong commented 7 years ago

The problem seems to have been resolved. Please reopen it if similar bugs come into being.