10
45
Fork
You've already forked doipjs
28

[NEW CLAIM] Wikipedia/MediaWiki #123

Closed
opened 2025年02月08日 23:25:57 +01:00 by quokka · 16 comments
Contributor
Copy link

Service provider

Name: Wikipedia/MediaWiki

Short description: Wikipedia or any other MediaWiki wiki.

Website: https://en.wikipedia.org among others

API documentation: https://en.wikipedia.org/api/ (for the English Wikipedia)

Proposed verification mechanism

Proposal 1

  • Add a template called {{Keyoxide proof}} or similar to any wiki. This template will be empty.
  • To any user page, add {{Keyoxide proof|PROOF}}, where PROOF is the proof
  • If it's on User: Example's page, then we query https://en.wikipedia.org/wiki/User:Example?action=raw to get the raw Wikitext, and see if there are any proofs
Example
{{Keyoxide proof|First proof}}
{{Keyoxide proof|Second proof}}
<!-- Rest of the user page -->

Proposal 2

  • Add a <span class="keyoxide-proof"> to the userpage, containing a proof.
  • If it's on User: Example's page, then we query https://en.wikipedia.org/wiki/User:Example?action=raw to get the raw Wikitext, and see if there are any proofs
Example
<span class="keyoxide-proof" style="display: none">First proof</span>
<span class="keyoxide-proof" style="display: none">Second proof</span>
<!-- Rest of the user page -->

Proposal 3

  • Add a subpage to your userpage called /Keyoxide proof
  • Edit it to only contain the proof(s). If the user wants multiple proofs, they can put each proof on a new line.
  • If it's on User: Example's page, then we query https://en.wikipedia.org/wiki/User:Example/Keyoxide_proof?action=raw to get the raw Wikitext, which should just be the proof(s) in plaintext.
Example
First proof
Second proof

Proposal 4

  • Add a subpage to your userpage called /Keyoxide proof.json
  • Edit it to contain the proof(s) in JSON format
  • If it's on User: Example's page, then we query https://en.wikipedia.org/wiki/User:Example/Keyoxide_proof.json?action=raw to get the raw JSON, which should contain the proof(s) in JSON.
Example
[
 "First proof",
 "Second proof"
]

Remarks

  • This was proposed previously in the forum, where they suggested using the RealMe MediaWiki extension, but I dislike that for several reasons:
    1. You can only add HTTP(S) URLs
    2. Not every wiki has that extension installed
  • Proposal 1 is suboptimal, because not all wikis allow for template creation by all users, and creating a new template on every wiki will be exhausting
  • Proposals 1 and 2 and 3 are suboptimal, because any user can edit them
  • Proposal 4 is my favourite, because it's the most universal and machine-readable
  • The template and subpage names are arbitrary, they don't necessarily need to be Keyoxide proof
  • At least on the English Wikipedia, JSON user pages can only be edited by the user themselves, or by an interface admin
  • I am still quite unfamiliar with Keyoxide, so please tell me if I missed something here!

Tasks

### Service provider Name: Wikipedia/MediaWiki Short description: Wikipedia or any other MediaWiki wiki. Website: https://en.wikipedia.org among others API documentation: https://en.wikipedia.org/api/ (for the English Wikipedia) ### Proposed verification mechanism #### Proposal 1 * Add a template called `{{Keyoxide proof}}` or similar to any wiki. This template will be empty. * To any user page, add `{{Keyoxide proof|PROOF}}`, where `PROOF` is the proof * If it's on `User: Example`'s page, then we query `https://en.wikipedia.org/wiki/User:Example?action=raw` to get the raw Wikitext, and see if there are any proofs ##### Example ```mediawiki {{Keyoxide proof|First proof}} {{Keyoxide proof|Second proof}} <!-- Rest of the user page --> ``` #### Proposal 2 * Add a `<span class="keyoxide-proof">` to the userpage, containing a proof. * If it's on `User: Example`'s page, then we query `https://en.wikipedia.org/wiki/User:Example?action=raw` to get the raw Wikitext, and see if there are any proofs ##### Example ```html <span class="keyoxide-proof" style="display: none">First proof</span> <span class="keyoxide-proof" style="display: none">Second proof</span> <!-- Rest of the user page --> ``` #### Proposal 3 * Add a subpage to your userpage called `/Keyoxide proof` * Edit it to only contain the proof(s). If the user wants multiple proofs, they can put each proof on a new line. * If it's on `User: Example`'s page, then we query `https://en.wikipedia.org/wiki/User:Example/Keyoxide_proof?action=raw` to get the raw Wikitext, which should just be the proof(s) in plaintext. ##### Example ``` First proof Second proof ``` #### Proposal 4 * Add a subpage to your userpage called `/Keyoxide proof.json` * Edit it to contain the proof(s) in JSON format * If it's on `User: Example`'s page, then we query `https://en.wikipedia.org/wiki/User:Example/Keyoxide_proof.json?action=raw` to get the raw JSON, which should contain the proof(s) in JSON. ##### Example ```json [ "First proof", "Second proof" ] ``` <!-- Optional, only fill in if you already know which APIs to use, etc --> ### Remarks * [This was proposed previously in the forum](https://community.keyoxide.org/d/338-add-mediawiki), where they suggested using the RealMe MediaWiki extension, but I dislike that for several reasons: 1. You can only add HTTP(S) URLs 2. Not every wiki has that extension installed * Proposal 1 is suboptimal, because not all wikis allow for template creation by all users, and creating a new template on every wiki will be exhausting * Proposals 1 and 2 and 3 are suboptimal, because any user can edit them * Proposal 4 is my favourite, because it's the most universal and machine-readable * The template and subpage names are arbitrary, they don't necessarily need to be `Keyoxide proof` * At least on the English Wikipedia, JSON user pages can only be edited by the user themselves, or by an interface admin * I am still quite unfamiliar with Keyoxide, so please tell me if I missed something here! ### Tasks <!-- Leave the following unchecked --> - [ ] Verification mechanism tested - [ ] Added to [doip-js](https://codeberg.org/keyoxide/doipjs) - [ ] Added to [doip-rs](https://codeberg.org/keyoxide/doip-rs) - [ ] Added proxy routes (if needed) - [ ] Added to [keyoxide-brands](https://codeberg.org/keyoxide/keyoxide-brands) - [ ] Added to [documentation](https://codeberg.org/keyoxide/keyoxide-docs)
Author
Contributor
Copy link

Proposal 1.1

  • Add a template called {{Keyoxide proof}} or similar to any wiki. This template will be empty.
  • To any user page, add {{Keyoxide proof|PROOF}}, where PROOF is the proof
  • If it's on User: Example's page, then we query https://en.wikipedia.org/wiki/User:Example?action=raw to get the raw Wikitext, and see if there are any proofs
Example
{{Keyoxide proof|First proof|Second proof}}
<!-- Rest of the user page -->

Remarks

  • This is basically the same as the first proposal, but this template could take multiple parameters instead.
#### Proposal 1.1 * Add a template called `{{Keyoxide proof}}` or similar to any wiki. This template will be empty. * To any user page, add `{{Keyoxide proof|PROOF}}`, where `PROOF` is the proof * If it's on `User: Example`'s page, then we query `https://en.wikipedia.org/wiki/User:Example?action=raw` to get the raw Wikitext, and see if there are any proofs ##### Example ```mediawiki {{Keyoxide proof|First proof|Second proof}} <!-- Rest of the user page --> ``` ### Remarks * This is basically the same as the first proposal, but this template could take multiple parameters instead.
Author
Contributor
Copy link

Idea for proposal 4:

We could query https://en.wikipedia.org/w/api.php (or any other wiki) with the following parameters:

{
	"action": "query",
	"format": "json",
	"prop": "revisions",
	"titles": "User:Example/Keyoxide proof.json",
	"formatversion": "2",
	"rvprop": "user|content",
	"rvslots": "*",
	"rvlimit": "1",
	"rvdir": "older"
}

This will get:

  • The namespace of the page (should be 2 for users)
  • The content model (should be json)
  • The person who last edited the page (should match the user we're trying to confirm)
  • The content of the page (escaped JSON)

The response should look something like:

{
 "batchcomplete": true,
 "query": {
 "pages": [
 {
 "pageid": 163334,
 "ns": 2,
 "title": "User:Quokka's test account/sandbox.json",
 "revisions": [
 {
 "user": "Quokka's test account",
 "slots": {
 "main": {
 "contentmodel": "json",
 "contentformat": "application/json",
 "content": "[\n\t\"some proof\"\n]"
 }
 }
 }
 ]
 }
 ]
 }
}

This could also fix the issue of randomers editing with Proposal 3... This would also technically work for 1 and 2, but there's a chance you won't notice that somebody else has edited your proof and then you edit something else on your page.

Idea for proposal 4: We could query `https://en.wikipedia.org/w/api.php` (or any other wiki) with the following parameters: ```json { "action": "query", "format": "json", "prop": "revisions", "titles": "User:Example/Keyoxide proof.json", "formatversion": "2", "rvprop": "user|content", "rvslots": "*", "rvlimit": "1", "rvdir": "older" } ``` This will get: * The namespace of the page (should be `2` for users) * The content model (should be `json`) * The person who last edited the page (should match the user we're trying to confirm) * The content of the page (escaped JSON) The response should look something like: ```json { "batchcomplete": true, "query": { "pages": [ { "pageid": 163334, "ns": 2, "title": "User:Quokka's test account/sandbox.json", "revisions": [ { "user": "Quokka's test account", "slots": { "main": { "contentmodel": "json", "contentformat": "application/json", "content": "[\n\t\"some proof\"\n]" } } } ] } ] } } ``` This could also fix the issue of randomers editing with Proposal 3... This would also technically work for 1 and 2, but there's a chance you won't notice that somebody else has edited your proof and then you edit something else on your page.
Author
Contributor
Copy link

Reopen (accidentally closed)

Reopen (accidentally closed)

I brought some other MediaWiki instances to test (IndieWeb, Microformats, SelfHTML).
Tested the Proposal 4 on SelfHTML today. I cannot use ?action=raw here so Proposal 2 - 4 won't be doable.
(Aside of this experiment being in violance of their terms in https://wiki.selfhtml.org/wiki/Hilfe:Wiki/Wikistruktur#Wie_erstellt_man_eine_Benutzerunterseite.3F so I'm going to delete the subpage)

I brought some other MediaWiki instances to test (IndieWeb, Microformats, SelfHTML). Tested the Proposal 4 on SelfHTML today. I cannot use `?action=raw` here so Proposal 2 - 4 won't be doable. (Aside of this experiment being in violance of their terms in https://wiki.selfhtml.org/wiki/Hilfe:Wiki/Wikistruktur#Wie_erstellt_man_eine_Benutzerunterseite.3F so I'm going to delete the subpage)

I tried the API playground on that wiki but receive a Forbidden on each and every request.

Hm, could be tough if neither option is available.

I tried the API playground on that wiki but receive a Forbidden on each and every request. Hm, could be tough if neither option is available.
Author
Contributor
Copy link

@Ryuno-Ki wrote in #123 (comment):

I tried the API playground on that wiki but receive a Forbidden on each and every request.

Hm, could be tough if neither option is available.

Well, the API playground may be inaccessible, but the API is... See https://wiki.selfhtml.org/api.php?action=query&format=json&titles=wiki for example

@Ryuno-Ki wrote in https://codeberg.org/keyoxide/doipjs/issues/123#issuecomment-2788150: > I tried the API playground on that wiki but receive a Forbidden on each and every request. > > Hm, could be tough if neither option is available. Well, the API playground may be inaccessible, but the API is... See https://wiki.selfhtml.org/api.php?action=query&format=json&titles=wiki for example

Oh. You're right.
https://wiki.selfhtml.org/api.php?action=query&format=json&prop=revisions&titles=Benutzer:Ryuno-Ki/Keyoxide_proof.json&formatversion=2&rvprop=content&rvlimit=1&rvdir=older (I don't have the sysop permissions to delete it - however I created a Discussion page in case someone is looking) is yielding the content.

https://wiki.selfhtml.org/api.php?action=query&format=json&prop=revisions&titles=Benutzer:Ryuno-Ki/Keyoxide_proof.json&formatversion=2&rvprop=user&rvlimit=1&rvdir=older is listing my username.

rvslots is unrecognised by the API and omitted here.

Oh. You're right. https://wiki.selfhtml.org/api.php?action=query&format=json&prop=revisions&titles=Benutzer:Ryuno-Ki/Keyoxide_proof.json&formatversion=2&rvprop=content&rvlimit=1&rvdir=older (I don't have the sysop permissions to delete it - however I created a Discussion page in case someone is looking) is yielding the content. https://wiki.selfhtml.org/api.php?action=query&format=json&prop=revisions&titles=Benutzer:Ryuno-Ki/Keyoxide_proof.json&formatversion=2&rvprop=user&rvlimit=1&rvdir=older is listing my username. `rvslots` is unrecognised by the API and omitted here.
Author
Contributor
Copy link

@Ryuno-Ki: My idea now is this:

The claim on the PGP key will simply be a URL to the JSON page on the wiki.

Thanks to sebbu from the chatroom, I found out that there's a reliable way to get the API endpoint of any MediaWiki wiki: There's a tag <link rel="EditURI"> on every page, whose href is the API endpoint with the parameter ?action=rsd.

So now we have the API endpoint, and we query the newest version using the following parameters:

{
	"action": "query",
	"format": "json",
	"prop": "revisions",
	"titles": "User:Example/Keyoxide proof.json",
	"formatversion": "2",
	"rvprop": "user|content",
	"rvlimit": "1",
	"rvdir": "older"
}

Which will return the user who last edited the page (query.pages[0].revisions[0].user), the content model (query.pages[0].revisions[0].contentmodel, should be json), and current content (query.pages[0].revisions[0].content escaped JSON string. Can be parsed with JSON.parse()).

Not using rvslots does work, but on some wikis it results in a warning: Because "rvslots" was not specified, a legacy format has been used for the output. This format is deprecated, and in the future the new format will always be used..

We can also do the following query:

{
	"action": "query",
	"format": "json",
	"prop": "revisions",
	"titles": "User:Example/Keyoxide proof.json",
	"formatversion": "2",
	"rvprop": "user",
	"rvlimit": "1",
	"rvdir": "newer"
}

To get the user who originally created the page (``query.pages[0].revisions[0].user`).

(Yes, it's counterintuitive, but older gets you the newest version and newer gets you the oldest version...)

@Ryuno-Ki: My idea now is this: The claim on the PGP key will simply be a URL to the JSON page on the wiki. Thanks to sebbu from the chatroom, I found out that there's a reliable way to get the API endpoint of any MediaWiki wiki: There's a tag `<link rel="EditURI">` on every page, whose `href` is the API endpoint with the parameter `?action=rsd`. So now we have the API endpoint, and we query the newest version using the following parameters: ```json { "action": "query", "format": "json", "prop": "revisions", "titles": "User:Example/Keyoxide proof.json", "formatversion": "2", "rvprop": "user|content", "rvlimit": "1", "rvdir": "older" } ``` Which will return the user who last edited the page (`query.pages[0].revisions[0].user`), the content model (`query.pages[0].revisions[0].contentmodel`, should be `json`), and current content (`query.pages[0].revisions[0].content` escaped JSON string. Can be parsed with `JSON.parse()`). Not using `rvslots` does work, but on some wikis it results in a warning: `Because "rvslots" was not specified, a legacy format has been used for the output. This format is deprecated, and in the future the new format will always be used.`. We can also do the following query: ```json { "action": "query", "format": "json", "prop": "revisions", "titles": "User:Example/Keyoxide proof.json", "formatversion": "2", "rvprop": "user", "rvlimit": "1", "rvdir": "newer" } ``` To get the user who originally created the page (``query.pages[0].revisions[0].user`). (Yes, it's counterintuitive, but `older` gets you the newest version and `newer` gets you the oldest version...)
Author
Contributor
Copy link

@Ryuno-Ki wrote in #123 (comment):

I brought some other MediaWiki instances to test (IndieWeb, Microformats, SelfHTML). Tested the Proposal 4 on SelfHTML today. I cannot use ?action=raw here so Proposal 2 - 4 won't be doable. (Aside of this experiment being in violance of their terms in https://wiki.selfhtml.org/wiki/Hilfe:Wiki/Wikistruktur#Wie_erstellt_man_eine_Benutzerunterseite.3F so I'm going to delete the subpage)

Oh also, any page can be queried with the API, making it no longer a problem to do any of them.

@Ryuno-Ki wrote in https://codeberg.org/keyoxide/doipjs/issues/123#issuecomment-2788103: > I brought some other MediaWiki instances to test (IndieWeb, Microformats, SelfHTML). Tested the Proposal 4 on SelfHTML today. I cannot use `?action=raw` here so Proposal 2 - 4 won't be doable. (Aside of this experiment being in violance of their terms in https://wiki.selfhtml.org/wiki/Hilfe:Wiki/Wikistruktur#Wie_erstellt_man_eine_Benutzerunterseite.3F so I'm going to delete the subpage) Oh also, any page can be queried with the API, making it no longer a problem to do any of them.
Author
Contributor
Copy link

OK I think this is my final proposal for how this would be done...

User

  1. User edits a page (e.g. their sandbox) to just the identity proof
  2. User goes to the page history and gets the URL of the specific revision (e.g. https://en.wikipedia.org/w/index.php?title=TITLE&oldid=REVID where REVID is some ID. The title does not matter)
  3. They add proof@ariadne.id=https://en.wikipedia.org/w/index.php?title=TITLE&oldid=REVID to their key

Keyoxide

  1. It gets the revision ID from the oldid parameter from the provided URL

  2. index.php and api.php have the same path, so it's trivial to find the API endpoint. Examples of what I mean:

    • https://en.wikipedia.org/w/index.phphttps://en.wikipedia.org/w/api.php
    • https://wiki.selfhtml.org/index.phphttps://wiki.selfhtml.org/api.php
    • https://indieweb.org/wiki/index.phphttps://indieweb.org/wiki/api.php
  3. It will query the endpoint with the following params:

{
 "action": "query",
 "format": "json",
 "prop": "revisions",
 "revids": "REVID",
 "rvprop": "user|content",
 "formatversion": "1"
}

where REVID is replaced by the revision ID.

  1. it checks if the revision exists. If it doesn't, query will contain badrevids (query.badrevids)
  2. If it does exist, we only care about Object.values(response.query.pages)[0].revisions[0]. We'll call it proofRevision
  3. We get the user who made the edit (proofRevision.user)
  4. We get the content of the edit (proofRevision["*"]), which should just be a string containing the proof
OK I think this is my final proposal for how this would be done... ## User 1. User edits a page (e.g. their sandbox) to just the identity proof 2. User goes to the page history and gets the URL of the specific revision (e.g. `https://en.wikipedia.org/w/index.php?title=TITLE&oldid=REVID` where REVID is some ID. The title does not matter) 3. They add `proof@ariadne.id=https://en.wikipedia.org/w/index.php?title=TITLE&oldid=REVID` to their key ## Keyoxide 1. It gets the revision ID from the `oldid` parameter from the provided URL 2. `index.php` and `api.php` have the same path, so it's trivial to find the API endpoint. Examples of what I mean: * `https://en.wikipedia.org/w/index.php` &rarr; `https://en.wikipedia.org/w/api.php` * `https://wiki.selfhtml.org/index.php` &rarr; `https://wiki.selfhtml.org/api.php` * `https://indieweb.org/wiki/index.php` &rarr; `https://indieweb.org/wiki/api.php` 3. It will query the endpoint with the following params: ```json { "action": "query", "format": "json", "prop": "revisions", "revids": "REVID", "rvprop": "user|content", "formatversion": "1" } ``` where `REVID` is replaced by the revision ID. 4. it checks if the revision exists. If it doesn't, `query` will contain `badrevids` (`query.badrevids`) 5. If it does exist, we only care about `Object.values(response.query.pages)[0].revisions[0]`. We'll call it `proofRevision` 6. We get the user who made the edit (`proofRevision.user`) 7. We get the content of the edit (`proofRevision["*"]`), which should just be a string containing the proof
Author
Contributor
Copy link

In MediaWiki, you can delete information about a given revision, including the user, comment, and content.

Tested with revision deletion. This is what is seen in proofRevision when a deleted revision is queried:

  1. Hidden username
{
 "userhidden": "",
 "contentformat": "text/x-wiki",
 "contentmodel": "wikitext",
 "*": "openpgp4fpr:..."
}
  1. Hidden content
{
 "user": "Example",
 "texthidden": ""
}
  1. Both hidden
{
 "userhidden": "",
 "texthidden": ""
}

Revision IDs on deleted articles are tested as non-existent revision IDs.

Maybe we should add checks for that too.

In MediaWiki, you can delete information about a given revision, including the user, comment, and content. Tested with revision deletion. This is what is seen in `proofRevision` when a deleted revision is queried: 1. Hidden username ```json { "userhidden": "", "contentformat": "text/x-wiki", "contentmodel": "wikitext", "*": "openpgp4fpr:..." } ``` 2. Hidden content ```json { "user": "Example", "texthidden": "" } ``` 3. Both hidden ```json { "userhidden": "", "texthidden": "" } ``` Revision IDs on deleted articles are tested as non-existent revision IDs. Maybe we should add checks for that too.

This file works for me:

/*
Copyright 2025 Daniel Levi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
 * MediaWiki service provider
 * @module serviceProviders/mediawiki
 * @example
 * import { ServiceProviderDefinitions } from 'doipjs';
 * const sp = ServiceProviderDefinitions.data.mediawiki.processURI('https://test.wikipedia.org/w/index.php?title=User:Quokka%27s_test_account/sandbox&oldid=643659');
 */
import * as E from '../enums.js'
import { ServiceProvider } from '../serviceProvider.js'
export const reURI = /^https?:\/\/(.*)\/index\.php\?(?:[^&]*&)*oldid=([^&#]+)(?:&.*)?$/
/**
 * @function
 * @param {string} uri - Claim URI to process
 * @returns {ServiceProvider} The service provider information based on the claim URI
 */
export function processURI (uri) {
 const match = uri.match(reURI)
 return new ServiceProvider({
 about: {
 id: 'mediawiki',
 name: 'Mediawiki wiki',
 homepage: null
 },
 profile: {
 display: 'Unknown username',
 uri: null,
 qr: null
 },
 claim: {
 uriRegularExpression: reURI.toString(),
 uriIsAmbiguous: true
 },
 proof: {
 request: {
 uri,
 fetcher: E.Fetcher.HTTP,
 accessRestriction: E.ProofAccessRestriction.NOCORS,
 data: {
 url: `https://${match[1]}/api.php?action=query&format=json&formatversion=2&prop=revisions&revids=${match[2]}&rvprop=user%7Ccontent%7ccomment`,
 format: E.ProofFormat.JSON
 }
 },
 response: {
 format: E.ProofFormat.JSON
 },
 target: [
 {
 format: E.ClaimFormat.URI,
 encoding: E.EntityEncodingFormat.PLAIN,
 relation: E.ClaimRelation.EQUALS,
 path: ['query', 'pages', 'revisions', 'content']
 }
 ]
 }
 })
}
export const functions = {
 postprocess: async (claimData, proofData, opts) => {
 claimData.profile.display = proofData.result.query.pages[0].revisions[0].user
 return { claimData, proofData }
 }
}

Test it using this script on a REPL (after adding the export to the serviceProvider index)

const doip = await import("./src/index.js");
const url = "https://test.wikipedia.org/w/index.php?title=User:Quokka%27s_test_account/sandbox&oldid=643694";
const fp = "openpgp4fpr:4993568a993436c36d0d77c6c8e368337d68194a";
const claim = new doip.Claim(url, fp);
claim.match();
await claim.verify();
console.log(claim.toJSON());
This file works for me: ```js /* Copyright 2025 Daniel Levi Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** * MediaWiki service provider * @module serviceProviders/mediawiki * @example * import { ServiceProviderDefinitions } from 'doipjs'; * const sp = ServiceProviderDefinitions.data.mediawiki.processURI('https://test.wikipedia.org/w/index.php?title=User:Quokka%27s_test_account/sandbox&oldid=643659'); */ import * as E from '../enums.js' import { ServiceProvider } from '../serviceProvider.js' export const reURI = /^https?:\/\/(.*)\/index\.php\?(?:[^&]*&)*oldid=([^&#]+)(?:&.*)?$/ /** * @function * @param {string} uri - Claim URI to process * @returns {ServiceProvider} The service provider information based on the claim URI */ export function processURI (uri) { const match = uri.match(reURI) return new ServiceProvider({ about: { id: 'mediawiki', name: 'Mediawiki wiki', homepage: null }, profile: { display: 'Unknown username', uri: null, qr: null }, claim: { uriRegularExpression: reURI.toString(), uriIsAmbiguous: true }, proof: { request: { uri, fetcher: E.Fetcher.HTTP, accessRestriction: E.ProofAccessRestriction.NOCORS, data: { url: `https://${match[1]}/api.php?action=query&format=json&formatversion=2&prop=revisions&revids=${match[2]}&rvprop=user%7Ccontent%7ccomment`, format: E.ProofFormat.JSON } }, response: { format: E.ProofFormat.JSON }, target: [ { format: E.ClaimFormat.URI, encoding: E.EntityEncodingFormat.PLAIN, relation: E.ClaimRelation.EQUALS, path: ['query', 'pages', 'revisions', 'content'] } ] } }) } export const functions = { postprocess: async (claimData, proofData, opts) => { claimData.profile.display = proofData.result.query.pages[0].revisions[0].user return { claimData, proofData } } } ``` Test it using this script on a REPL (after adding the export to the serviceProvider index) ```js const doip = await import("./src/index.js"); const url = "https://test.wikipedia.org/w/index.php?title=User:Quokka%27s_test_account/sandbox&oldid=643694"; const fp = "openpgp4fpr:4993568a993436c36d0d77c6c8e368337d68194a"; const claim = new doip.Claim(url, fp); claim.match(); await claim.verify(); console.log(claim.toJSON()); ```

While this is technically working:

/*
Copyright 2025 Daniel Levi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
 * MediaWiki service provider
 * @module serviceProviders/mediawiki
 * @example
 * import { ServiceProviderDefinitions } from 'doipjs';
 * const sp = ServiceProviderDefinitions.data.mediawiki.processURI('https://test.wikipedia.org/w/index.php?title=User:Quokka%27s_test_account/sandbox&oldid=643659');
 */
import * as E from '../enums.js'
import { ServiceProvider } from '../serviceProvider.js'
export const reURI = /^https?:\/\/(.*)\/index\.php\?(?:[^&]*&)*oldid=([^&#]+)(?:&.*)?$/
/**
 * @function
 * @param {string} uri - Claim URI to process
 * @returns {ServiceProvider} The service provider information based on the claim URI
 */
export function processURI (uri) {
 const match = uri.match(reURI)
 return new ServiceProvider({
 about: {
 id: 'mediawiki',
 name: 'Mediawiki wiki',
 homepage: null
 },
 profile: {
 display: 'Unknown username',
 uri: null,
 qr: null
 },
 claim: {
 uriRegularExpression: reURI.toString(),
 uriIsAmbiguous: true
 },
 proof: {
 request: {
 uri,
 fetcher: E.Fetcher.HTTP,
 accessRestriction: E.ProofAccessRestriction.NOCORS,
 data: {
 url: `https://${match[1]}/api.php?action=query&format=json&formatversion=2&prop=revisions&revids=${match[2]}&rvprop=user%7Ccontent%7ccomment`,
 format: E.ProofFormat.JSON
 }
 },
 response: {
 format: E.ProofFormat.JSON
 },
 target: [
 {
 format: E.ClaimFormat.URI,
 encoding: E.EntityEncodingFormat.PLAIN,
 relation: E.ClaimRelation.EQUALS,
 path: ['query', 'pages', 'revisions', 'content']
 }
 ]
 }
 })
}
export const functions = {
 postprocess: async (claimData, proofData, opts) => {
 const match = claimData.proof.request.uri.match(reURI);
 const response = await fetch(`https://${match[1]}/api.php?action=query&format=json&formatversion=2&meta=siteinfo&siprop=general%7Cnamespaces`);
 const siteInfo = await response.json();
 claimData.about.name = siteInfo.query.general.sitename;
 claimData.about.homepage = siteInfo.query.general.base;
 const userName = [
 siteInfo.query.namespaces["2"].name,
 proofData.result.query.pages[0].revisions[0].user
 ].join(":");
 claimData.profile.display = userName;
 claimData.profile.uri = `https://${match[1]}/index.php?title=${encodeURIComponent(userName. replace(" ", "_"))}`;
 return { claimData, proofData }
 }
}

I would like to repurpose the existing library (with set UserAgent etc.)

/*
Copyright 2025 Daniel Levi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
 * MediaWiki service provider
 * @module serviceProviders/mediawiki
 * @example
 * import { ServiceProviderDefinitions } from 'doipjs';
 * const sp = ServiceProviderDefinitions.data.mediawiki.processURI('https://test.wikipedia.org/w/index.php?title=User:Quokka%27s_test_account/sandbox&oldid=643659');
 */
import mergeOptions from 'merge-options';
import { opts as _opts } from '../defaults.js';
import * as E from '../enums.js'
import { fetch as fetchProof } from '../proofs.js'
import { ServiceProvider } from '../serviceProvider.js'
export const reURI = /^https?:\/\/(.*)\/index\.php\?(?:[^&]*&)*oldid=([^&#]+)(?:&.*)?$/
/**
 * @function
 * @param {string} uri - Claim URI to process
 * @returns {ServiceProvider} The service provider information based on the claim URI
 */
export function processURI (uri) {
 const match = uri.match(reURI)
 return new ServiceProvider({
 about: {
 id: 'mediawiki',
 name: 'Mediawiki wiki',
 homepage: null
 },
 profile: {
 display: 'Unknown username',
 uri: null,
 qr: null
 },
 claim: {
 uriRegularExpression: reURI.toString(),
 uriIsAmbiguous: true
 },
 proof: {
 request: {
 uri,
 fetcher: E.Fetcher.HTTP,
 accessRestriction: E.ProofAccessRestriction.NOCORS,
 data: {
 url: `https://${match[1]}/api.php?action=query&format=json&formatversion=2&prop=revisions&revids=${match[2]}&rvprop=user%7Ccontent%7ccomment`,
 format: E.ProofFormat.JSON
 }
 },
 response: {
 format: E.ProofFormat.JSON
 },
 target: [
 {
 format: E.ClaimFormat.URI,
 encoding: E.EntityEncodingFormat.PLAIN,
 relation: E.ClaimRelation.EQUALS,
 path: ['query', 'pages', 'revisions', 'content']
 }
 ]
 }
 })
}
export const functions = {
 postprocess: async (claimData, proofData, opts) => {
 const match = claimData.proof.request.uri.match(reURI);
 const data = mergeOptions(claimData.toJSON(), {
 proof: {
 request: {
 data: {
 url: `https://${match[1]}/api.php?action=query&format=json&formatversion=2&meta=siteinfo&siprop=general%7Cnamespaces`
 }
 }
 }
 });
 const options = mergeOptions(_opts, {});
 const siteInfo = await fetchProof(data, options);
 claimData.about.name = siteInfo.result.query.general.sitename;
 claimData.about.homepage = siteInfo.result.query.general.base;
 const userName = [
 siteInfo.result.query.namespaces["2"].name,
 proofData.result.query.pages[0].revisions[0].user
 ].join(":");
 claimData.profile.display = userName;
 claimData.profile.uri = `https://${match[1]}/index.php?title=${encodeURIComponent(userName. replace(" ", "_"))}`;
 return { claimData, proofData }
 }
}
While this is technically working: ```js /* Copyright 2025 Daniel Levi Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** * MediaWiki service provider * @module serviceProviders/mediawiki * @example * import { ServiceProviderDefinitions } from 'doipjs'; * const sp = ServiceProviderDefinitions.data.mediawiki.processURI('https://test.wikipedia.org/w/index.php?title=User:Quokka%27s_test_account/sandbox&oldid=643659'); */ import * as E from '../enums.js' import { ServiceProvider } from '../serviceProvider.js' export const reURI = /^https?:\/\/(.*)\/index\.php\?(?:[^&]*&)*oldid=([^&#]+)(?:&.*)?$/ /** * @function * @param {string} uri - Claim URI to process * @returns {ServiceProvider} The service provider information based on the claim URI */ export function processURI (uri) { const match = uri.match(reURI) return new ServiceProvider({ about: { id: 'mediawiki', name: 'Mediawiki wiki', homepage: null }, profile: { display: 'Unknown username', uri: null, qr: null }, claim: { uriRegularExpression: reURI.toString(), uriIsAmbiguous: true }, proof: { request: { uri, fetcher: E.Fetcher.HTTP, accessRestriction: E.ProofAccessRestriction.NOCORS, data: { url: `https://${match[1]}/api.php?action=query&format=json&formatversion=2&prop=revisions&revids=${match[2]}&rvprop=user%7Ccontent%7ccomment`, format: E.ProofFormat.JSON } }, response: { format: E.ProofFormat.JSON }, target: [ { format: E.ClaimFormat.URI, encoding: E.EntityEncodingFormat.PLAIN, relation: E.ClaimRelation.EQUALS, path: ['query', 'pages', 'revisions', 'content'] } ] } }) } export const functions = { postprocess: async (claimData, proofData, opts) => { const match = claimData.proof.request.uri.match(reURI); const response = await fetch(`https://${match[1]}/api.php?action=query&format=json&formatversion=2&meta=siteinfo&siprop=general%7Cnamespaces`); const siteInfo = await response.json(); claimData.about.name = siteInfo.query.general.sitename; claimData.about.homepage = siteInfo.query.general.base; const userName = [ siteInfo.query.namespaces["2"].name, proofData.result.query.pages[0].revisions[0].user ].join(":"); claimData.profile.display = userName; claimData.profile.uri = `https://${match[1]}/index.php?title=${encodeURIComponent(userName. replace(" ", "_"))}`; return { claimData, proofData } } } ``` I would like to repurpose the existing library (with set UserAgent etc.) ```js /* Copyright 2025 Daniel Levi Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** * MediaWiki service provider * @module serviceProviders/mediawiki * @example * import { ServiceProviderDefinitions } from 'doipjs'; * const sp = ServiceProviderDefinitions.data.mediawiki.processURI('https://test.wikipedia.org/w/index.php?title=User:Quokka%27s_test_account/sandbox&oldid=643659'); */ import mergeOptions from 'merge-options'; import { opts as _opts } from '../defaults.js'; import * as E from '../enums.js' import { fetch as fetchProof } from '../proofs.js' import { ServiceProvider } from '../serviceProvider.js' export const reURI = /^https?:\/\/(.*)\/index\.php\?(?:[^&]*&)*oldid=([^&#]+)(?:&.*)?$/ /** * @function * @param {string} uri - Claim URI to process * @returns {ServiceProvider} The service provider information based on the claim URI */ export function processURI (uri) { const match = uri.match(reURI) return new ServiceProvider({ about: { id: 'mediawiki', name: 'Mediawiki wiki', homepage: null }, profile: { display: 'Unknown username', uri: null, qr: null }, claim: { uriRegularExpression: reURI.toString(), uriIsAmbiguous: true }, proof: { request: { uri, fetcher: E.Fetcher.HTTP, accessRestriction: E.ProofAccessRestriction.NOCORS, data: { url: `https://${match[1]}/api.php?action=query&format=json&formatversion=2&prop=revisions&revids=${match[2]}&rvprop=user%7Ccontent%7ccomment`, format: E.ProofFormat.JSON } }, response: { format: E.ProofFormat.JSON }, target: [ { format: E.ClaimFormat.URI, encoding: E.EntityEncodingFormat.PLAIN, relation: E.ClaimRelation.EQUALS, path: ['query', 'pages', 'revisions', 'content'] } ] } }) } export const functions = { postprocess: async (claimData, proofData, opts) => { const match = claimData.proof.request.uri.match(reURI); const data = mergeOptions(claimData.toJSON(), { proof: { request: { data: { url: `https://${match[1]}/api.php?action=query&format=json&formatversion=2&meta=siteinfo&siprop=general%7Cnamespaces` } } } }); const options = mergeOptions(_opts, {}); const siteInfo = await fetchProof(data, options); claimData.about.name = siteInfo.result.query.general.sitename; claimData.about.homepage = siteInfo.result.query.general.base; const userName = [ siteInfo.result.query.namespaces["2"].name, proofData.result.query.pages[0].revisions[0].user ].join(":"); claimData.profile.display = userName; claimData.profile.uri = `https://${match[1]}/index.php?title=${encodeURIComponent(userName. replace(" ", "_"))}`; return { claimData, proofData } } } ```
Author
Contributor
Copy link

Omg thanks so much for this! When I get home from school I'll test this out!

Omg thanks so much for this! When I get home from school I'll test this out!
Author
Contributor
Copy link

PR for this: (削除) #124 (削除ここまで) (追記) #129 (追記ここまで)

PR for this: <del>https://codeberg.org/keyoxide/doipjs/pulls/124</del> <ins>https://codeberg.org/keyoxide/doipjs/pulls/129</ins>
Author
Contributor
Copy link

Merged

Merged
Sign in to join this conversation.
dev
main
dev
fix-irc-fetcher
new-claim-eveonline-143
prepare-new-release
fix-jsdoc-types
yarn-to-npm
support-openpgp-aspe-claims
support-html-alias
improve-activitypub-support
v1-restructure
into-es-module
support-aspe
fix-js-lsp-issues
improve-linting
add-markers
support-opencollective-claim
support-entity-decoding
use-rome-tools
support-cloudflare-buster
support-fediverse-posts
matrix-room-verification
2.1.0
2.1.0-rc.3
2.1.0-rc.2
2.1.0-rc.1
2.0.1
2.0.0
2.0.0-rc.1
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.1.1
1.1.0
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
v0.19.1-alpha.0
0.19.0
0.18.3
0.18.2
0.18.1
0.18.0
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.0
0.13.0
0.12.9
0.12.8
0.12.7
0.12.6
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
0.11.2
0.11.1
0.11.0
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.5
0.8.4
0.8.3
0.8.1
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.0
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
Labels
Clear labels
bug
Something is not working

Archived

enhancement
New feature

Archived

Contribution welcome
Get started contributing here
Good first issue
Good if you are new to the project or to open source contributions
Impact
External
Affects the people using the project
Impact
Internal
Affects on the people working on the project
Priority
Critical
Work on right now
Priority
High
Work on at earliest convenience
Priority
Low
Work on in spare time
Priority
Medium
Work on regularly
Review
Duplicate
Already exists
Review
Off Topic
Does not fall within the scope of the repo/project
Status
Backlog
Is not being worked on yet
Status
Blocked
Is waiting on something or someone
Status
Completed
Is done
Status
In Progress
Is being worked on
Status
Investigating
Is waiting on research or questions
Status
Needs Decision
Is waiting on a decision by the devs/contributors
Status
Needs Info
Is waiting on additional information before it can be solved
Status
Needs Triage
Is new issue that needs reviewing
Status
Testing
Is being checked and verified
Status
Waiting For Review
Is waiting on reviewers to approve
Status
Won't Fix
Won't be fixed
Type
Bug
Related to something not working as intended
Type
CI
Related to continuous integration
Type
Documentation
Related to documentation
Type
Enhancement
Related to a new feature or an improved one
Type
New Claim
Related to a new identity claim/proof
Type
Security
Related to security
Type
Tests
Related to code tests
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
keyoxide/doipjs#123
Reference in a new issue
keyoxide/doipjs
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?