Page MenuHomePhabricator

Enable OAuth registration with a Wikimedia account on Wikidocumentaries wiki
Open, Needs TriagePublic


The goal is to allow user contributions to Wikimedia projects from the Wikidocumentaries app.

Wikidocumentaries has two distinct parts: the front end app and the Wikibase backend. Authorizing is ideally done between the front end, the Wikibase, and the Wikimedia environment. This can also be tackled in smaller pieces.

  1. Allow authorizing from the Wikidocumentaries front end app to the Wikimedia environment to make edits using the Wikidocumentaries app
  2. Log into the local Wikidocumentaries wiki
  3. Connect local account to Wikimedia
  4. Connect to other sources such as OpenStreetMap

Event Timeline

Susannaanas renamed this task from Enable OAuth registration on Wikidocumentaries wiki to Enable OAuth registration with a Wikimedia account on Wikidocumentaries wiki.Aug 4 2019, 7:41 AM
Susannaanas updated the task description. (Show Details)
Tgr added a subscriber: Tgr.

Our plan to do the same for Wikispore is to use the OAuthAuthentication extension (T110460: Update OAuthAuthentication to use AuthManager needs to be finished first). Depending on how much of T197969: User registration tasks you want to coexist with OAuth-based identities, that might or might not be the right solution for you.

Notes from the meeting with @MikkoRinne and @Tgr

Library versions
+ "express-session": "^1.16.2",
+ "full-icu": "^1.2.1",
+ "passport-mediawiki-oauth": "^0.1.0"

Passport source of the latest version

Clarifying errors
Session needed:
It turned out that sessions are no longer supported within express, so the correct approach was to separately install "express-session".

Console may also give:
Oauthcallback: 1: Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
Server gives the familiar:
InternalOAuthError: Error: Not enough or too many segments

at /Users/Mikko/Dev/dinador/wikimedia/test/wikidocumentaries-topic-search-api/node_modules/passport-mediawiki-oauth/lib/passport-mediawiki-oauth/oauth.js:172:22
at passBackControl (/Users/Mikko/Dev/dinador/wikimedia/test/wikidocumentaries-topic-search-api/node_modules/passport-mediawiki-oauth/lib/passport-mediawiki-oauth/oauth-patches.js:114:15)
at IncomingMessage.<anonymous> (/Users/Mikko/Dev/dinador/wikimedia/test/wikidocumentaries-topic-search-api/node_modules/passport-mediawiki-oauth/lib/passport-mediawiki-oauth/oauth-patches.js:134:13)
at emitNone (events.js:111:20)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1064:12)
at _combinedTickCallback (internal/process/next_tick.js:139:11)
at process._tickCallback (internal/process/next_tick.js:181:9)

The affected consumer is 30e51ed81f7c57c5c266d37c3a48e65f, right?

One thing worth noting it that you can pass extra query parameters in oauth_callback (if you set the "Allow consumer to specify a callback in requests and use "callback" URL above as a required prefix." flag for the consumer) and they will be passed back. Maybe passport wants to use that to maintain some state?

That is the correct consumer.

Looks like the important error was removed and the less important ones kept in the list above. This was the latest point where the process got stuck:

Wrong number of parameters returned:

With local testing the URL returned looks like:
With the npm-library the browser shows {"error":"Internal Server Error"} and the wikidocumentaries-api barfs:

InternalOAuthError: Error: Not enough or too many segments
    at /Users/Mikko/Dev/dinador/wikimedia/test/wikidocumentaries-topic-search-api/node_modules/passport-mediawiki-oauth/lib/passport-mediawiki-oauth/oauth.js:172:22
    at passBackControl (/Users/Mikko/Dev/dinador/wikimedia/test/wikidocumentaries-topic-search-api/node_modules/passport-mediawiki-oauth/lib/passport-mediawiki-oauth/oauth-patches.js:114:15)
    at IncomingMessage.<anonymous> (/Users/Mikko/Dev/dinador/wikimedia/test/wikidocumentaries-topic-search-api/node_modules/passport-mediawiki-oauth/lib/passport-mediawiki-oauth/oauth-patches.js:134:13)
    at emitNone (events.js:111:20)
    at IncomingMessage.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1064:12)
    at _combinedTickCallback (internal/process/next_tick.js:139:11)
    at process._tickCallback (internal/process/next_tick.js:181:9)

Based on googling it appears that at least some version expects to get back exactly three parameters, whereas we are getting two (oauth_verifier and oauth_token).

One possibility, which was discussed, would be to skip using the libraries and store the two parameters, but I'll first look into passing extra query parameters, as commented by @Tgr above.

I did further testing on this, especially checking manually what is going on in the third (and failing) exchange of oauth_verifier to access token and secret. The first few error messages from the API are rather clear:

  1. An error occurred in the OAuth protocol: Missing timestamp parameter. The parameter is required
  2. An error occurred in the OAuth protocol: Missing nonce parameter. The parameter is required
  3. An error occurred in the OAuth protocol: No signature method parameter. This parameter is required

I added them as:

oauth_timestamp: Math.round((new Date()).getTime() / 1000.0),
oauth_nonce: crypto.randomBytes(16).toString('base64'),
oauth_signature_method: 'HMAC-SHA1'

...which looked acceptable. But then it gets tricky. The next error message is:

  • Error: An error occurred in the OAuth protocol: Invalid signature

The MediaWiki instructions for developers say this:

To do this, send a request to Special:OAuth/token which includes the oauth_verifier parameter you just received and is signed with the application token and secret and the request token and secret.

Later on the instructions are referencing section 9 of the OAuth1.0 specification:

Steps 1 and 3 of the authorization process require signing the request; API requests and Special:OAuth/identify must likewise be signed. The signing process is detailed in section 9 of the OAuth spec, ...

In the spec we learn e.g. that:

  • The signature method is not mandated by the specification; Service Providers are free to implement and document their own methods (MediaWiki instructions don't explicitly say which signature method is being used, but HMAC-SHA1 is probably a reasonable guess)
  • A signature base string is formed by concatenating almost all request parameters in alphabetical order. This includes "Parameters in the OAuth HTTP Authorization header" (MediaWiki instructions do not define what should be set in the authorization header), "Parameters in the HTTP POST request body" (MediaWiki instructions do not define which request body parameters are expected to be set - actually there's not even a precise mention of whether it should be GET or POST, but POST seems to be working)
  • "signed with the application token and secret and the request token and secret" doesn't define precisely, what these fields should be called, which is necessary to know especially if they should be bit-exactly concatenated to the signature base string in alphabetical order. Checking e.g. the oauth instruction for Twitter, they give the precise instructions to put the tokens into the data-field of a POST request. That would be understandable and repeatable. "Signed with" doesn't specify precisely, whether these fields should be concatenated into an oauth_signature parameter as specified in the OAuth1.0 specification, or just listed in the data body of an HTTP POST, as used by e.g. Twitter.

As the only feedback coming from the API is "Invalid signature", this spans a practically infinite search space of different authorization headers, request body parameters, parameter names and an optional oauth_version parameter, which may or may not be included, but according to the specification may have to be included in the signature base string, as the signature base string is specified as including everything else except the oauth_signature itself.

I tested also the python example given in the slides referenced by the MediaWiki developer instructions. Exactly the same thing happens as with the manual testing with node: While copying the received URL to the input of Step 2 on slide 12, the timestamp is missing, the nonce is missing and the signature method is missing. After all three are filled into the URL, the process fails with an Invalid signature. End of story.

Since none of the existing examples are working, it gives the impression that the OAuth API has changed after the instructions were written. In the end the developer is left with documentation, which wasn't exact at the time it was written, and which may no longer correspond to the current implementation.

Forgetting the old libraries, the OAuth process may be doable using e.g. the latest version of passport-oauth (instead of the five-year-old passport-mediawiki-oauth), but also there the range of parameters is practically infinite as long as it is not known, what exactly is meant by "signed with" in MediaWiki.

Is this related to the recent changes in authentication? Anyone with experiences, libraries, parameters for deploying OAuth with please share your wisdom. Pinging @Abbe98 @Zache @Chicocvenancio @Maxlath @Majavah...