OAuth/For Developers/zh

此页说明如何开发可与安装了 的MediaWiki (一个使MediaWiki拥有 OAuth 服务器功能的扩展插件) 交互的应用程序，以安全地向用户请求代替其操作的权限.

OAuth简述
OAuth 允许应用程序向用户请求许可. 在不记录用户密码的同时，通过用户的权限进行操作，缺不能随意使用该用户的任意权限(如应用程序可以编辑文章, 却不可以删除它们. 这样, 高权限的用户可以放心地使用带有OAuth功能的工具)

上述情况可以通过 OAuth 2.0 或 OAuth 1.0a 协议授权, 它包含三个部分:


 * 1) 开发者必须先在Wiki上注册应用程序(在OAuth术语中常被称为"消费者"), 可能会经过一些审核流程, 随后收到一些凭证.
 * 2) 当用户想要使用它时, 应用程序必须经过授权流程.  这将引导用户至Wiki上的特殊页面，该页面将显示授权对话框.  如果用户同意授权，则应用程序将收到另一组凭据（仅属于该用户，并且可以随时被用户吊销）.
 * 3) 当应用程序需要代替用户执行操作(通过API请求)时, 它可以使用步骤1和2中收到的凭证对请求进行签名.

OAuth是一种广泛使用的开放标准(你可以在Google, Facebook或Github等网站上看到它, 例如使用这些网站的账号登录其他站点时) It is not to be confused with OATH (a second-factor authentication protocol, commonly known as "type the six numbers you see on your mobile app", now enabled on Wikimedia sites) and OpenID Connect (an authentication protocol based on OAuth 2.0 - the OAuth MediaWiki extension does include a somewhat OpenID-like custom protocol for determining user identity though).

For a slightly larger nutshell, see these slides.

OAuth详情
在注册时, OAuth 1.0a和OAuth 2的流程基本相同，仅有一些表单项有差异. 其余部分视OAuth版本而有很大不同.

注册
To register a new OAuth application, submit the form at. Try to give sufficient information about the application for admins and users to decide whether it can be trusted. URLs to places with more information (such as the application itself, the source code or on-wiki documentation) can be useful. For OAuth applications to be used on Wikimedia projects, see app guidelines.

Besides the descriptive ones, the fields have the following meaning:


 * This consumer is for use only by : for owner-only consumers (which do not need to be reviewed or authorized but are only usable by yourself)
 * callback URL: the URL where the user returns after authorization is checked against this. (This is an extra layer of security against an attacker trying to steal credentials during authorization.) 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.  ).
 * Applicable project (for wiki farms only): 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 authorize all permissions together (T59505).
 * Allowed IP ranges: OAuth requests not matching this will be rejected. (This is an optional extra layer of security against an attacker stealing your application's credentials and trying to impersonate it.) One of the few settings that you'll be able to change later.
 * Public RSA key (OAuth 1.0a only): 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. 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. The list of applications (approved or otherwise) is public and can be browsed at.

认证
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. To get them, you need to go through the authorization process, which consists of three steps:
 * 1) Get a request token from the wiki by sending a GET request to , signed with the application key and secret, with the callback URL (where the user will be sent after a successful authorization) passed as the   query parameter (if you have set a constant URL at registration, the value of the parameter must be  ). If you are successful, the response will be a JSON object with   and   fields - the request token and request secret. (If not, it will have an   field.)
 * 2) Ask the user to authorize the application by sending them to , with the application token and request token passed as query parameters (  and  , respectively). The user will see an authorization dialog with some basic information about the application and the list of grants, and can decide to authorize or cancel.
 * 3) If the user did choose to authorize, 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   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  which includes the   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 authorization process at any time though.)

Applications which only need minimal privileges (have been registered as User identity verification only) can use  instead of   in step 2. This works the same way but the user will only see the authorization dialog if they have not authorized this application before; otherwise the authorization 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.

作为用户身份执行请求
To take advantage of the authorization, 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 authorizing 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.

鉴别用户
The OAuth extension includes a custom protocol (similar to OpenID Connect) for authenticating the user. To use this, send a signed OAuth request to ; the response will be a JWT (a signed JSON object) including the name of the user, their central ID (under the key  ) 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 matches the domain name of the wiki, the audience  matches your application key, the issued-at time  is in the past and reasonably close to current time, the expiration time  is in the future, the nonce  matches the one you sent in the request.

Signing requests
Steps 1 and 3 of the authorization process require signing the request; API requests and   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 (authorization step 3) or consumer token/secret only (authorization step 1)

Authorization
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:
 * 1) Ask the user to authorize the application by sending them to   under the wiki's REST endpoint (usually  ), with   and the consumer key (also called the client application key) as the , and ideally also a   and  . If your consumer is non-confidential, you'll also need to include a PKCE code challenge. The user will see an authorization dialog with some basic information about the application and the list of grants, and can decide to authorize or cancel.
 * 2) If the user did choose to authorize, 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   will contain the authorization code that you can use to fetch the access token. To do this, send a request to   under the wiki's REST endpoint (usually  ), including , the   parameter you just received, your  , your client authentication (typically as   and, for confidential clients,  ), and if non-confidential the PKCE code verifier. 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 authorization process at any time though.)

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 authorization, 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 authorizing 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  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 custom protocol (similar to OpenID Connect) for authenticating the user. To use this, send an authenticated OAuth request to  under the wiki's REST endpoint (usually  ); the response will include the name of the user and various other information (such as their user groups and whether they are blocked).

Setting up a development environment
OAuth is now available in your MediaWiki-Vagrant development environment. Add the  role, and your wiki will be able to authorize OAuth apps.

Alternatively, you can register an OAuth application on beta meta and test your code against that.

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 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 authorization when not using RSA, and during app registration).
 * 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

 * oauthclient-php - OAuth client library maintained by Wikimedia
 * OAuth - PECL client library
 * firebase/php-jwt - a popular JWT library
 * laravel-socialite-mediawiki - MediaWiki driver for Laravel's Socialite library.

Python

 * flask-mwoauth - a Flask blueprint to run OAuth against MediaWiki's Extension:OAuth (See tutorial: My first Flask OAuth tool)
 * MediaWiki-OAuth (mwoauth) - on top of requests-oauthlib
 * python-social-auth - auth framework that includes a Mediawiki backend (Example tutorial: My first Django OAuth tool)
 * pyjwt - a JWT library

Ruby

 * omniauth-mediawiki - a MediaWiki strategy for OmniAuth (also available as a gem)

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

 * mrjones/oauth

Java

 * ScribeJava since version 5.5.0 (pull request)

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  declarations.)

Before Starting:

Python Toolforge tutorial using mwoauth
See: wikitech:Help:Toolforge/My first Flask OAuth tool

Go command-line client using mrjones/auth
Before you begin:

Full applications using OAuth

 * Weekipedia, A full scale React.js port of the Wikipedia mobile site uses both.
 * many Toolforge tools