Jump to content

OAuth/開発者向け解説

From mediawiki.org
This page is a translated version of the page OAuth/For Developers and the translation is 31% complete.

このページでは、MediaWikiサーバーへの操作リクエストを安全に行うために使用される、Extension:OAuth (MediaWikiをOAuthサーバーに対応させる拡張機能) がインストールされたウィキにおいて、アプリケーションを開発する方法を解説します。

ボットまたはアプリケーションの開発において同じコンシューマーが同じ利用者アカウントを必ず使用する場合、オーナー専用コンシューマーがよりシンプルで使いやすい場合があります。

OAuthの概要

OAuthは、ユーザー個人のパスワードを使用せずに、ユーザーのウィキアカウントを通して操作許可をリクエストするためのアプリケーションです。OAuthを使用すると、ユーザーがフロントエンド上で行える操作の一部のみを限定してリクエストできます。(例として、アプリケーションに記事の編集操作を許可し、削除操作は許可しないという使い方が挙げられます。これにより、上位の操作権限を有するユーザーも、OAuthを使用すると安全にツールを運用できます。)

本拡張機能はOAuth 2.0またはOAuth 1.0a使用し、3つのコンポーネントから構成されています:

  1. 開発者はアプリケーションをウィキに登録する必要があり、場合に応じたレビュープロセスを経て、資格情報を取得します。(ここでの「開発者」は、OAuthの用語では「コンシューマー」としばしば呼ばれます。)
  2. 運用時は、アプリケーションの認証プロセスを経る必要があります。 このプロセスではユーザーが特別ページに転送され、認証ダイアログの操作がリクエストされます。 ユーザーがこれを許可すると、アプリケーションに対し資格情報が付与されます。(この資格情報は当該ユーザー専用で、必要に応じていつでもユーザー自身が剥奪できます。)
  3. アプリケーションが実際にユーザーの代わりに操作 (APIリクエスト) を行う際、ステップ1と2から得た資格情報を用いてリクエストに対する署名が行われます。

OAuthは広く使用されているオープン・スタンダードです。(Google、Facebook、GitHubなどもこれを使用しており、例としてこれらのサイトのアカウントを使用し、別のサイトにログインする場合などに用いられています。) なお、OATH (二段階認証プロトコル、「モバイル機器に表示された6桁の番号の入力してください」というメッセージで知られているもの)、およびOpenID Connect (OAuth 2.0ベースの認証プロトコル、OAuthの拡張機能が部分的にサポート) とは異なります。

OAuth 1.0aに関してのより詳細な情報は、スライドをご覧ください。

OAuthの詳細

登録プロセスは、入力フォームフィールドの一部の内容を除き、OAuth 1.0aとOAuth 2とで基本的に同一です。ただし、この他の部分はOAuthのバージョンにより大きく異なります。

登録

新しくOAuthアプリケーションを登録する場合、Special:OAuthConsumerRegistration/proposeからフォームを送信してください。 アプリケーションの承認審査を円滑にするため、フォームには必要情報を十分に入力してください。 追加情報が得られるURL (アプリケーション自体のウェブサイト、ソースコード、ウィキ上のドキュメンテーションなど) を提供することがより望まれます。 アプリケーションをウィキメディアプロジェクト上で運用する場合は、ガイドラインも参照してください。

記述式以外のフィールドについては、以下の内容を入力してください:

  • This consumer is for use only by ‎<user>: オーナー専用コンシューマーを使用するか否か (レビューおよび認証が不要となりますが、自身以外は使用不可能になります)。
  • callback URL: 認証完了後にユーザーがリダイレクトされるURL。 (これは、認証の処理段階で資格情報が盗まれることを防ぐための、追加のセキュリティ設定です。) If the use as prefix option is enabled, the URL must start with this (the check is dumb so make sure to add at least a / after the domain name), otherwise it must be an exact match. If you are developing/testing your local machine, specify Localhost in this field (e.g. http://localhost:8080/).
  • Applicable project (for wiki farms only): project(s) on which to use your application. You can limit the application to a single wiki or have it work everywhere.
  • Types of grants / Applicable grants: the permissions needed by your application (be conservative). The actual permissions will be an intersection of this and what permissions the user has. At this time, the user must authorise all permissions together (T59505).
  • Allowed IP ranges: an optional specification of the IP ranges that you use, where OAuth requests not matching this will be rejected. This is an extra layer of security against an attacker stealing your application's credentials and trying to impersonate it. This is one of the few settings that you'll be able to change later.
  • Public RSA key (OAuth 1.0a のみ): public key used by your application for signing requests. You can just leave this empty (most applications do) to use a slightly simpler shared-secret mechanism instead. This is one of the few settings that you'll be able to change later. After registration, you'll receive the credentials needed to use OAuth. You will be able to use it immediately with your own user account (this is meant for testing); others will only be able to use it once it is approved by an administrator.

If you have changed your mind, you can disable the application under Special:OAuthConsumerRegistration/list. The list of applications (approved or otherwise) is public and can be browsed at Special:OAuthListConsumers.


OAuth 1.0a

認証

Authorisation dialog shown to users

When registering the application, you receive two pieces of credentials: the application token (a public ID for the application) and the application secret (sort of like a password).

To be able to identify a user or make API requests in their name, you need to get another set of credentials (these ones specific to that user): the access token and access secret.[1]

To get them, you need to go through the authorisation process, which consists of three steps:[2]

  1. Get a request token from the wiki by sending a GET request to Special:OAuth/initiate, signed with the application key and secret, with the callback URL (where the user will be sent after a successful authorisation) passed as the oauth_callback query parameter (if you have set a constant URL at registration, the value of the parameter must be oob).[3] If you are successful, the response will be a JSON object with token and key fields—the request token and request secret. (If not, it will have an error field.)
  2. Ask the user to authorise the application by sending them to Special:OAuth/authorize, with the application token and request token passed as query parameters (oauth_consumer_key and oauth_token, respectively).[4] The user will see an authorisation dialog with some basic information about the application and the list of grants, and can decide to authorise or cancel.
  3. If the user did choose to authorise, they will be redirected to the callback URL you have given (at registration, or as a URL parameter in step 1). A query parameter called oauth_verifier will contain the verification code that you can use to exchange the request token and secret for the access token and secret. To do this, send a request to Special:OAuth/token[3] which includes the oauth_verifier parameter you just received and is signed with the application token and secret and the request token and secret. The response will contain the access token/secret (in the same format as the request token/secret in step 1).

The access token and secret is what you'll need to sign API requests. The request token and secret is not useful anymore and can be discarded. The access token will remain valid indefinitely, unless the user revokes it. (If you prefer not to store it, you can just repeat the authorisation process at any time though.)

Applications which only need minimal privileges (have been registered as User identity verification only) can use /authenticate instead of /authorize in step 2. This works the same way, but the user will only see the authorisation dialog if they have not authorised this application before; otherwise the authorisation will silently succeed.

Chances are whatever language/framework you are using will have a library to support this procedure, so you don't have to implement it manually—each step will be a single function call. See below for examples.

Making requests on the user's behalf

To take advantage of the authorisation, requests have to be signed with the application token/secret and access token/secret. When that's successfully done, the wiki will treat the request as if it was made by the authorising user. Only API requests can be made via OAuth, with one exception (see next section). Certain API modules which would not make sense with OAuth (such as login/logout) or would allow privilege escalation (such as the centralauthtoken API) are disabled.

Applications which need minimal privileges (have been registered as User identity verification only) cannot use the API at all.

Identifying the user

The OAuth extension includes a custom protocol (similar to OpenID Connect) for authenticating the user. To use this, send a signed OAuth request to Special:OAuth/identify:[3] the response will be a JWT (a signed JSON object) including the name of the user, their central ID (under the key sub) and various other information (such as their user groups and whether they are blocked; also the email address if the application was registered with the right grant type). This is more secure than using the API (e.g. the userinfo module) for authentication, which could be subject to man-in-the-middle attacks; always use this if you need to identify a user! Also, make sure you properly validate the JWT (there are many libraries which can help with that). You should check each of the following: the issuer (iss) matches the domain name of the wiki, the audience (aud) matches your application key, the issued-at time (iat) is in the past and reasonably close to current time, the expiration time (exp) is in the future, the nonce (nonce) matches the one you sent in the request.

Signing requests

Steps 1 and 3 of the authorisation 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, but it is cumbersome to do implement by hand and many libraries are available. You can find code samples and an overview of how to do it by hand in the owner-only consumer documentation. (That is for signing with the consumer token/secret and access token/secret. Modify as appropriate to sign with the consumer token/secret and request token/secret (authorisation step 3) or consumer token/secret only (authorisation step 1).)

OAuth 2

Authorisation

When registering the application, you receive two pieces of credentials: the client application key (a public ID for the application, also called the client ID or consumer key) and the client application secret (a confidential password). To be able to identify a user or make API requests in their name, you need to get another credential (this one specific to that user): the access token. To get it, you need to go through the the OAuth 2 Authorization Code flow, which consists of two steps:[5]

  1. Ask the user to authorise the application by sending them to oauth2/authorize under the wiki's REST endpoint (usually rest.php), with response_type=code and the consumer key (also called the client application key) as the client_id, possibly a state if you want, and optionally the redirect_uri (if yes, it must be the same as in your application request). If your consumer is non-confidential, you'll also need to include a PKCE code challenge (code_challenge and code_challenge_method=S256). The user will see an authorisation dialog with some basic information about the application and the list of grants, and can decide to authorise or cancel.
  2. If the user did choose to authorise, they will be redirected to the callback URL you have given (at registration, or as an URL parameter in step 1). A query parameter called code will contain the authorisation code that you can use to fetch the access token. To do this, send a POST request to oauth2/access_token under the wiki's REST endpoint (usually rest.php), including grant_type=authorization_code, the code parameter you just received, your client authentication (typically as client_id and, for confidential clients, client_secret), the redirect_uri if and only if you specified it in the previous step (must be the same value for both), and if non-confidential the PKCE code_verifier and code_challenge_method. The response will contain the access token and a refresh token.

The access token is what you'll need to send future API requests. The refresh token can be used to fetch a new access token if the original access token expires. If you prefer not to store either token, you can just repeat the authorisation process at any time. (Note that non-confidential clients can currently only use refresh tokens using their client secret keys, not using their client IDs only, see T323855.)

Chances are whatever language/framework you are using will have a library to support this procedure so you don't have to implement it manually - each step will be a single function call. See below for examples.


Making requests on the user's behalf

To take advantage of the authorisation, requests have to include the access token. When that's successfully done, the wiki will treat the request as if it was made by the authorising user. Only API requests can be made via OAuth 2. Certain API modules which would not make sense with OAuth (such as login/logout) or would allow privilege escalation (such as the centralauthtoken API) are disabled. If the access token is used to fetch a CSRF token or other tokens, the access token must still be passed (as a header) with requests that use those tokens.

Applications which need minimal privileges (have been registered as User identity verification only) cannot use the API at all.

API requests including rest.php/oauth2/resource/profile must be authenticated with an HTTP Authorization header containing the access token, like

Authorization: Bearer abcde....6789

Identifying the user

The OAuth extension includes a somewhat incomplete[6] implementation of OpenID Connect for authenticating the user. To use this, send an authenticated OAuth GET request to the oauth2/resource/profile API (MediaWiki's implementation of what the OIDC spec calls the UserInfo enpoint) under the wiki's REST endpoint (usually rest.php); the response will include the name of the user and various other information. Note that the GET request must use the HTTP Authorization header, not a query string token.

  • sub (central user id)
  • username
  • editcount
  • confirmed_email
  • blocked
  • registered
  • groups
  • rights
  • realname (only if user granted permission)
  • email (only if user granted permission)


Setting up a development environment

You can register an OAuth application on beta meta and test your code against that.

If you want to improve the extension itself, or debug protocol issues in detail, OAuth is available in the MediaWiki-Vagrant development environment. Add the oauth role, and your local wiki will be able to authorise OAuth apps.

$ vagrant roles enable oauth
$ vagrant provision

Once the code is nearly ready, you can register an OAuth application on the real wiki. You will be able to test it with the same user account used for registering, even before it gets reviewed by admins.

If you are creating an application for Wikimedia projects, consider hosting it at Wikimedia Toolforge, a free tool forge and hosting platform for Wikimedia-related services.


Security benefits and trade-offs

  • Unlike password-based authentication, the OAuth 1.0 protocol prevents any man-in-the-middle attack even if the wiki does not require HTTPS: all interactions between MediaWiki and the application are signed with either a shared secret (using HMAC-SHA1), or a public key (RSA). HTTPS is still required for certain steps though (for obtaining the shared secret in the second step of authorisation when not using RSA, and during app registration). OAuth 2.0 does not involve signing and relies on HTTPS for security.
  • Actions via OAuth happen under the user's name but will be tagged with the application's name as well so rogue applications can be identified. Wiki admins can revoke the application's permissions (and thus bar it from any further action) if needed.
  • The user can revoke the permission of the application to use that specific user account if they don't like how it works or don't trust it anymore. Unlike a password change, this is not disruptive for the user.
  • Sufficiently flexible applications might allow users to circumvent IP blocks (since MediaWiki will see the IP of the application server, not that of the user). This can be handled the same way proxies are, by trusting XFF headers set by the application (see T159889 for some limitations).
  • The application secret must be kept secret. Submitting it to source control or putting it into user-accessible code (such as mobile app or desktop application; even if it is obfuscated) undermines the security model and will result in admins forcefully disabling the application. Exceptions are made for example applications demoing OAuth usage, if they are explicitly labeled as such and request limited rights.


ライブラリ

PHP

Python

Ruby

Rust

Node.js

  • passport-mediawiki-oauth - MediaWiki strategy for the Passport auth framework (which can be used effortlessly as a middleware with Express JS and similar frameworks)
  • oauth-fetch-json - library for signing OAuth requests

Go

Java


コード例

PHP client without using any libraries

OAuth Hello World – easy to understand demo application written in PHP without any libraries.

PHP command-line client with RSA keys, using oauthclient-php

PHP application using classes from the OAuth extension codebase. (TODO convert this to actually use oauthclient-php! Probably just a bunch of use declarations.)

Before Starting:

$ openssl genrsa -out appkey.pem 4096
$ openssl rsa -in appkey.pem -pubout > appkey.pub
PHP source code
<?php

if ( PHP_SAPI !== 'cli' ) {
	die( "CLI-only test script\n" );
}

/**
 * A basic client for overall testing
 */

function wfDebugLog( $method, $msg) {
	//echo "[$method] $msg\n";
}


require 'OAuth.php';
require 'MWOAuthSignatureMethod.php';

$consumerKey = '';
#$consumerSecret = ''; // We don't need this, since we're using RSA, except to validate the /identify call

$privateKey = file_get_contents( 'appkey.pem' );

$baseurl = 'http://<wiki>/wiki/Special:OAuth';
$endpoint_req = $baseurl . '/initiate?format=json&oauth_callback=oob'; // format=json makes php a little easier
$endpoint_acc = $baseurl . '/token?format=json';
$endpoint_id = $baseurl . '/identify';

$c = new OAuthConsumer( $consumerKey, $privateKey );

// Make sure we sign title and format
$parsed = parse_url( $endpoint_req );
$extraSignedParams = array();
parse_str($parsed['query'], $extraSignedParams);
$extraSignedParams['title'] = 'Special:OAuth/initiate';

$init_req = OAuthRequest::from_consumer_and_token(
	$c,                // OAuthConsumer for your app
	NULL,              // User token, NULL for calls to initiate
	"GET",             // http method
	$endpoint_req,     // endpoint url (this is signed)
	$extraSignedParams // extra parameters we want to sign (must include title)
);

$rsa_method = new MWOAuthSignatureMethod_RSA_SHA1( new OAuthDataStore(), $privateKey );
$init_req->sign_request(
	$rsa_method, // OAuthSignatureMethod
	$c,          // OAuthConsumer for your app
	NULL         // User token, NULL for calls to initiate
);

echo "Getting request token with: $init_req\n";

$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, (string) $init_req ); // Pass OAuth in GET params
curl_setopt( $ch, CURLOPT_HTTPGET, true );
curl_setopt( $ch, CURLOPT_HEADER, false );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
$data = curl_exec( $ch );

if( !$data ) {
	'Curl error: ' . curl_error( $ch );
}

echo "Returned: $data\n\n";


$requestToken = json_decode( $data );
print "Visit $baseurl/authorize?oauth_token={$requestToken->key}&oauth_consumer_key=$consumerKey\n";

// ACCESS TOKEN
print "Enter the verification code:\n";
$fh = fopen( "php://stdin", "r" );
$line = fgets( $fh );

$rc = new OAuthToken( $requestToken->key, $requestToken->secret );
$parsed = parse_url( $endpoint_acc );
parse_str($parsed['query'], $params);
$params['oauth_verifier'] = trim($line);
$params['title'] = 'Special:OAuth/token';

$acc_req = OAuthRequest::from_consumer_and_token(
	$c,
	$rc,
	"GET",
	$endpoint_acc,
	$params
);
$acc_req->sign_request($rsa_method, $c, $rc);

echo "Calling: $acc_req\n";

unset( $ch );
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $endpoint_acc );
curl_setopt( $ch, CURLOPT_HEADER, 0 );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_HTTPHEADER, array( $acc_req->to_header() ) ); // Set the Authorization Header
$data = curl_exec( $ch );
if( !$data ) {
	'Curl error: ' . curl_error( $ch );
}

echo "Returned: $data\n\n";
$acc = json_decode( $data );
$accessToken = new OAuthToken( $acc->key, $acc->secret );

/**
 * Insecurely call the api for information about the user. A MITM can
 * forge a response from the server, so don't rely on this for identity!
 */
$apiurl = 'http://<wiki>/w/api.php';
$apiParams = array(
	'action' => 'query',
	'meta' => 'userinfo',
	'uiprop' => 'rights',
	'format' => 'json',
);

$api_req = OAuthRequest::from_consumer_and_token(
	$c,           // Consumer
	$accessToken, // User Access Token
	"GET",        // HTTP Method
	$apiurl,      // Endpoint url
	$apiParams    // Extra signed parameters
);
$api_req->sign_request( $rsa_method, $c, $accessToken );

$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $apiurl . "?" . http_build_query( $apiParams ) );
curl_setopt( $ch, CURLOPT_HEADER, 0 );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_HTTPHEADER, array( $api_req->to_header() ) ); // Authorization header required for api
$data = curl_exec( $ch );
if( !$data ) {
	'Curl error: ' . curl_error( $ch );
}
echo "Returned: $data\n\n";


/**
 * Securely get the identity of the user
 */
$consumerSecret = '';

$extraSignedParams = array(
	'title' => 'Special:OAuth/identify'
);

$req = OAuthRequest::from_consumer_and_token(
	$c,
	$accessToken,
	"GET",
	$endpoint_id,
	$extraSignedParams
);
$req->sign_request( $rsa_method, $c, $accessToken );

echo "Calling:  '$endpoint_id'\nHeader: {$req->to_header()}\n\n";

$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $endpoint_id );
curl_setopt( $ch, CURLOPT_HEADER, 0 );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_HTTPHEADER, array( $req->to_header() ) );
$data = curl_exec( $ch );
if( !$data ) {
	'Curl error: ' . curl_error( $ch );
}

$identity = JWT::decode( $data, $consumerSecret );

// Validate the JWT
if ( !validateJWT( $identity, $consumerKey, $req->get_parameter( 'oauth_nonce' ) ) ) {
	print "The JWT did not validate";
} else {
	print "We got a valid JWT, describing the user as:\n";
	print " * Username: {$identity->username}\n";
	print " * User's current groups: " . implode( ',', $identity->groups ) . "\n";
	print " * User's current rights: " . implode( ',', $identity->rights ) . "\n";
}


/**
 * Validate a JWT, to ensure this isn't a reply, spoof, etc.
 * @param $identity the decoded JWT
 * @param $consumerKey your App's Key
 * @param $nonce the nonce sent with your request, which should be returned
 */
function validateJWT( $identity, $consumerKey, $nonce ) {

	$expectedConnonicalServer = 'http://<wiki>';

	// Verify the issuer is who we expect (server sends $wgCanonicalServer)
	if ( $identity->iss !== $expectedConnonicalServer ) {
		print "Invalid Issuer";
		return false;
	}

	// Verify we are the intended audience
	if ( $identity->aud !== $consumerKey ) {
		print "Invalid Audience";
		return false;
	}

	// Verify we are within the time limits of the token. Issued at (iat) should be
	// in the past, Expiration (exp) should be in the future.
	$now = time();
	if ( $identity->iat > $now || $identity->exp < $now ) {
		print "Invalid Time";
		return false;
	}

	// Verify we haven't seen this nonce before, which would indicate a replay attack
	if ( $identity->nonce !== $nonce ) {
		print "Invalid Nonce";
		return false;
	}

	return true;
}

Python command-line client using mwoauth

Python source code
from mwoauth import ConsumerToken, Handshaker
import requests
from requests_oauthlib import OAuth1
from six.moves import input  # For compatibility between python 2 and 3

# Consruct a "consumer" from the key/secret provided by MediaWiki
import config  # You'll need to provide this

#Create a file called config.py somewhere where it will be found by python, e.g. in the same directory as this script,
#with the following content (not including the # characters! )
#consumer_key = "the consumer token you got when you registered your applicaton"
#consumer_secret = "the secret token you got when you registered your application"

#For example:
#consumer_key = "20bc67da5081a30c736340c493f60d14"
#consumer_secret = "af4313371bb2fb38e81fe7d300080085f52849f9"

consumer_token = ConsumerToken(config.consumer_key, config.consumer_secret)

# Construct handshaker with wiki URI and consumer
handshaker = Handshaker("https://en.wikipedia.org/w/index.php",
                        consumer_token)

# Step 1: Initialise -- ask MediaWiki for a temporary key/secret for user
redirect, request_token = handshaker.initiate()

# Step 2: Authorise -- send user to MediaWiki to confirm authorisation
print("Point your browser to: %s" % redirect)  #
response_qs = input("Response query string: ")

# Step 3: Complete -- obtain authorised key/secret for "resource owner"
access_token = handshaker.complete(request_token, response_qs)

# Construct an auth object with the consumer and access tokens
auth1 = OAuth1(consumer_token.key,
               client_secret=consumer_token.secret,
               resource_owner_key=access_token.key,
               resource_owner_secret=access_token.secret)

# Now, accessing the API on behalf of a user
print("Reading top 10 watchlist items")
response = requests.get(
    "https://en.wikipedia.org/w/api.php",
    params={
        'action': "query",
        'list': "watchlist",
        'wllimit': 10,
        'wlprop': "title|comment",
        'format': "json"
    },
    auth=auth1
)
for item in response.json()['query']['watchlist']:
    print("{title}\t{comment}".format(**item))

Python Toolforge tutorial using mwoauth

以下を参照してください: wikitech:Help:Toolforge/My first Flask OAuth tool

Go command-line client using mrjones/auth

Before you begin:

$ go get github.com/mrjones/oauth
Go source code
package main

import (
	"fmt"
	"os"
	"github.com/mrjones/oauth"
	"io/ioutil"
	"strconv"
)

func main() {

	var consumerKey string = ""
	var consumerSecret string = ""

	if len(consumerKey) == 0 || len(consumerSecret) == 0 {
		os.Exit(1)
	}

	c := oauth.NewConsumer(
		consumerKey,
		consumerSecret,
		oauth.ServiceProvider{
			RequestTokenUrl:   "http://<wiki>/wiki/index.php/Special:OAuth/initiate",
			AuthorizeTokenUrl: "http://<wiki>/wiki/index.php/Special:OAuth/authorize",
			AccessTokenUrl:    "http://<wiki>/wiki/index.php/Special:OAuth/token",
		})

	c.Debug(true)

	c.AdditionalParams = map[string]string{
		"title":   "Special:OAuth/initiate",
	}

	c.AdditionalAuthorizationUrlParams = map[string]string{
		"oauth_consumer_key": consumerKey,
	}

	requestToken, url, err := c.GetRequestTokenAndUrl("oob")
	if err != nil {
		fmt.Println( err )
	}

	fmt.Println( "Got token " + requestToken.Token )

	fmt.Println("(1) Go to: " + url)
	fmt.Println("(2) Grant access, you should get back a verification code.")
	fmt.Println("(3) Enter that verification code here: ")

	verificationCode := ""
	fmt.Scanln(&verificationCode)

	c.AdditionalParams = map[string]string{
		"title":   "Special:OAuth/token",
	}

	accessToken, err := c.AuthorizeToken(requestToken, verificationCode)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println( "Got access token " + accessToken.Token )

	c.AdditionalParams = map[string]string{}

	response, err := c.Get(
		"http://<wiki>/wiki/api.php",
		map[string]string{
			"action": "query",
			"meta": "userinfo",
			"uiprop": "rights",
			"format": "json",
		},
		accessToken)

	if err != nil {
		fmt.Println(err)
	}

	fmt.Println( "\tResponse Status: '" + response.Status + "'\n" )
	fmt.Println( "\tResponse Code: " + strconv.Itoa(response.StatusCode) + "\n" )
	bytes, _ := ioutil.ReadAll(response.Body)
	fmt.Println( "\tResponse Body: " + string(bytes) + "\n" )

}

JavaScript applications using OAuth 2

Full applications using OAuth

注記

  1. With an RSA key the credentials are slightly different. It is assumed that people choosing to use an RSA key know what they are doing, so this tutorial will assume you have chosen the non-RSA option.
  2. This is sometimes called three-legged OAuth flow. In certain circumstances a simpler, one-step alternative (one-legged OAuth) is also available. See owner-only consumers for that.
  3. 3.0 3.1 3.2 Due to a bug you must currently use a non-nice URL such as en.wikipedia.org/w/index.php?title=Special:OAuth/initiate.
  4. Due to another bug you must use a nice URL here, such as en.wikipedia.org/wiki/Special:OAuth/authorize. A bit embarrassing, we know.
  5. This is sometimes called three-legged OAuth flow. In certain circumstances a simpler, one-step alternative (one-legged OAuth) is also available. See owner-only consumers for that.
  6. See T254063 for details.