OAuth/For Developers/ja

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

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

OAuthの概要

OAuth は、利用者のパスワードを知らずに、利用者のウィキ アカウントを通じて操作の許可をアプリケーションがリクエストできる仕組みであり、利用者ができるすべての操作を行えるわけではありません (例: アプリケーションは記事の編集はできても削除はできないなど)。そのため、拡張権限を持つ利用者も OAuth 対応ツールを安全に利用できます。

本拡張機能は \OAuth 2.0 または \OAuth 1.0a プロトコルを使用し、3 つの段階から構成されます:

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

OAuthは広く使用されているオープン・スタンダードです。(Google、Facebook、GitHubなどもこれを使用しており、例としてこれらのサイトのアカウントを使用し、別のサイトにログインする場合などに用いられています。)

OAuth を以下のものと混同しないでください:
  • OATH (二段階認証プロトコル、「モバイル機器に表示された6桁の番号の入力してください」というメッセージで知られているもの)
  • OpenID Connect (OAuth 2.0ベースの認証プロトコル、OAuthの拡張機能が部分的にサポート)

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

OAuthの詳細

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

登録

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

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

  • This consumer is for use only by <user>: オーナー専用コンシューマーを使用するか否かを指定します。 (レビューおよび認証が不要となりますが、自身以外は当該アプリケーションを使用不可になります)。
  • callback URL (オーナー専用コンシューマーを使用しない場合): 認証完了後に利用者が戻る URL はこれと照合されます。 (これは、認証の処理段階で資格情報が盗まれることを防ぐための、追加のセキュリティ設定です。) use as prefixオプションを有効化した場合、転送先のURLはここで設定したURLとの先頭一致が要求されます(このチェックは粗略なため、最低限/をドメイン名の後ろに含めてください)。無効化した場合、URLの完全一致が要求されます。 ローカル環境で開発やテストを行う場合、このフィールドにはLocalhostを指定してください(例: http://localhost:8080/)。
  • Applicable project (ウィキファームの場合のみ): アプリケーションを使用するプロジェクトを指定します。 特定のウィキのみに対象を制限することも、すべてのウィキで使用可能にすることもできます。
  • Types of grants / Applicable grants: アプリケーションが必要とする権限を指定します(慎重に選択してください)。実際に割り当てられる権限は、アカウントが所持している権限からに限定されます。 現在の仕様では、登録可能な利用者権限はグループ化されており、権限を個別に選択することはできません(T59505)。
  • Allowed IP ranges: OAuthリクエストを受け付けるIPレンジを任意で指定します(一致しないリクエストは拒否されます)。 これは、悪意をもった人物がアプリケーションの資格情報を盗み、なりすまそうとするのを防ぐための追加のセキュリティ対策です。 これは、のちに変更が可能な数少ない設定項目の一つです。
  • Public RSA key (OAuth 1.0aのみ): リクエストの署名に使用するアプリケーションの公開鍵を指定します。 この欄を空白にし、簡略化された共有シークレットメカニズムを使用することも可能です(ほとんどのアプリケーションがこの方法で登録されます)。 これは、のちに変更が可能な数少ない設定項目の一つです。 登録後、OAuthを使用するための資格情報が提供されます。 自身の利用者アカウントからは、当該アプリケーションを(テスト目的で)即座に使用可能になります。他利用者は、管理者の承認後に使用可能になります。

もし考えが変わった場合は、m:Special:OAuthConsumerRegistration/listからアプリケーションを無効化することができます。 アプリケーションの一覧は(承認の是非を問わず)公開されており、Special:OAuthListConsumersで閲覧することができます。

OAuth 1.0a

認証

利用者に表示される認証ダイアログ

アプリケーションを登録すると、アプリケーショントークン(アプリケーションの公開ID)とアプリケーションシークレット(一種のパスワード)の2つの資格情報が提供されます。

利用者の識別や、他者名義でAPIリクエストを行う必要がある場合は、追加で(当該利用者に固有の)アクセストークンとアクセスシークレットの取得が必要です。[1]

この情報の取得には、以下3つの認証プロセスが必要となります:[2]

  1. GET リクエストを Special:OAuth/initiate に送信して、ウィキからリクエスト トークンを取得します。リクエストにはアプリケーション キーとシークレットで署名し、認証成功後に利用者が転送されるコールバック URL を oauth_callback クエリ パラメーターとして渡します (登録時に固定の URL を設定している場合は、パラメーターの値を 必ず oob にする必要があります)。[3] リクエストに成功すると、tokenおよびkeyのフィールド(リクエストトークンとリクエストシークレット)が含まれるJSONオブジェクトがレスポンスとして返ります。 (失敗した場合、当該JSONオブジェクトはerrorのフィールドを含みます。)
  2. 利用者にアプリケーションの承認を求めるため、アプリケーション トークン (oauth_consumer_key) とリクエスト トークン (oauth_token) をクエリ パラメーターとして付けて、Special:OAuth/authorize に誘導します。[4] 利用者には、アプリケーションの基本情報と付与される権限の一覧が表示された認可ダイアログが表示され、承認するかキャンセルするかを選択できます。
  3. 利用者が承認した場合、登録時に指定したコールバック URL (またはステップ 1 で指定した URL パラメーター) に転送されます。 oauth_verifierというクエリパラメータには、リクエストトークンとリクエストシークレットをアクセストークンとアクセスシークレットへ交換するために使用可能な認証コードが含まれます。 この交換を行うには、Special:OAuth/token[3]にリクエストを送信します。これには先ほど受け取ったoauth_verifierパラメータを含め、アプリケーショントークンとアプリケーションシークレット、およびリクエストトークンとリクエストシークレットで署名を行います。 レスポンスとして、アクセストークンとアクセスシークレットが返ります(ステップ1のリクエストトークンおよびリクエストシークレットと同一形式です)。

このアクセストークンとアクセスシークレットが、APIリクエストの署名用途で必要になります。 リクエストトークンとリクエストシークレットは、この時点で不要となります。 アクセス トークンは、利用者が取り消さない限り、無期限に有効です。 (ただし、アプリケーションにこの情報を保持させたくない場合、一回ごとに認証プロセスを繰り返させる方式を取ることも可能です。)

要求する権限が最小限のアプリケーション (User identity verification only として登録されたもの) は、ステップ 2 で /authorize の代わりに /authenticate を使用できます。 これも同様の仕組みで動作しますが、利用者が以前にこのアプリケーションを承認していない場合にのみ認可ダイアログが表示されます。承認済みの場合は、認可は自動的に行われます。

アプリケーションの開発にいかなる言語やフレームワークを使用しようとも、上記の手順をサポートしているライブラリが存在する可能性が高いため、手動で実装する必要はありません。各ステップは1回の関数呼び出しで処理できます。 実例はこのページの下部をご覧ください。

利用者の代理でリクエストを送信

ここまでの認証情報を使用するには、HTTPリクエストをアプリケーショントークン/シークレットとアクセストークン/シークレットで署名する必要があります。 これが正常に完了すると、ウィキはそのリクエストを認可した利用者によって行われたものとして扱います。 OAuthを使用して行えるのは(後述の一つの例外を除き)APIリクエストのみです。 なお、OAuthを通すことに意味を成さない特定のAPIモジュール(login/logoutなど)や、利用者権限の範囲拡大をするAPIモジュール(centralauthtoken APIなど)は無効化されています。

User identity verification onlyとして登録されたアプリケーションは、APIを完全に使用できません。

利用者の識別

OAuth 拡張機能は、利用者認証のために (OpenID Connect に似た) 独自のプロトコルを含みます。 これを使用するには、署名されたOAuthリクエストをSpecial:OAuth/identifyへ送信してください:[3] レスポンスは、利用者名、中央管理ID(キーsubの下部)のほかに、さまざまな情報を含むJWT(JSON Web Token - 署名されたJSONオブジェクト)が返されます(この「情報」には、利用者グループやブロック状態、さらに権限付与設定で選択されている場合メールアドレスなども含まれます)。 これは中間者攻撃の対象となる可能性がある API (例: userinfo モジュール) を使った認証よりも安全です。利用者を識別する必要がある場合は、必ずこちらを使用してください。 なお、必ずJWTを適切に読み込める環境を構築してください(この助けとなるライブラリは多数あります)。 追加で、発行者(iss)がウィキのドメイン名と一致すること、受信者(aud)がアプリケーションキーと一致すること、発行時間(iat)が直近の過去時刻であること、期限(exp)が未来の時刻であること、ノンス(nonce)がリクエストで送信したものと一致することを確認してください。

リクエストに署名

認証プロセスのステップ1と3ではリクエストに署名をする必要があります。同様に、APIリクエストとSpecial:OAuth/identifyにも署名をする必要があります。 署名プロセスはOAuth仕様書のセクション9に詳細に記載されていますが、手動で実装するのには手間が掛かるため、多数存在するライブラリの使用が推奨されます。 一からで実装する方法についてのコードサンプルと概要は、オーナー専用コンシューマーのドキュメンテーションに記載されています。 (これは、コンシューマートークン/シークレットおよびアクセストークン/シークレットで署名するためのものです。 必要に応じ、コンシューマートークン/シークレットで署名をし、リクエストトークン/シークレットをリクエスト(認証ステップ3)、またはコンシューマートークン/シークレットのみをリクエスト(認証ステップ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).[6] 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 a 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 implementation of OpenID Connect for authenticating the user.[7] 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 Worldeasy 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 (OAuth 1.0)

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. RSA鍵を使用する場合、資格情報が少々異なります。 RSA鍵を使用する場合はその仕組みを理解していることが前提となるため、本チュートリアルではRSA鍵を選択しなかった場合を想定し解説します。
  2. これは、時より「三脚OAuthフロー」と呼ばれます。状況により、よりシンプルなワンステップの代替手段である「一脚OAuthフロー」も利用可能です。 詳細はオーナー専用コンシューマーをご覧ください。
  3. 1 2 3 現在は、phab:T59500 により en.wikipedia.org/w/index.php?title=Special:OAuth/initiate のように不格好な URL を使用する必要があります。
  4. phab:T74186 により、ここではen.wikipedia.org/wiki/Special:OAuth/authorizeのような「整った」URLを指定する必要があります。残念ながら、これが現在の仕様です。
  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. PKCE Proof Key for Code Exchange
  7. See T254063 for details.