[104] [DFN[[RUBYB[[[トークンエンドポイント]]]@en[token endpoint]]]]は、
[[クライアント]]に[[アクセストークン]]を発行する[[認可鯖]]上の[[エンドポイント]]です。

* 仕様書

[REFS[
- [7] [CITE@en[RFC 5849 - The OAuth 1.0 Protocol]] ([TIME[2014-12-28 14:19:21 +09:00]] 版) <http://tools.ietf.org/html/rfc5849#section-2.3>
- [201] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749>
-- [34] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-3.2>
-- [52] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-4.1.3>
-- [55] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-4.1.4>
-- [58] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-4.2.2>
-- [61] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-4.3.2>
-- [64] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-4.3.3>
-- [65] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-4.4.2>
-- [67] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-4.4.3>
-- [68] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-4.5>
-- [71] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-5.1>
-- [87] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-5.2>
-- [1] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-6>
-- [21] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-9>
-- [89] [CITE@en[RFC 6749 - The OAuth 2.0 Authorization Framework]] ([TIME[2014-12-15 14:15:35 +09:00]] 版) <http://tools.ietf.org/html/rfc6749#section-10>
- [202] [CITE@en[RFC 7521 - Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants]] ([TIME[2015-05-28 13:43:35 +09:00]] 版) <https://tools.ietf.org/html/rfc7521#section-4.1>
- [108] [CITE@en[Final: OpenID Connect Core 1.0 incorporating errata set 1]] ([TIME[2014-11-09 04:00:29 +09:00]] 版) <http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse>
- [109] [CITE@en[Final: OpenID Connect Core 1.0 incorporating errata set 1]] ([TIME[2014-11-09 04:00:29 +09:00]] 版) <http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication>
- [170] [CITE@en[Final: OAuth Request Body Hash]] ([TIME[2009-04-12 08:08:27 +09:00]] 版) <http://oauth.googlecode.com/svn&l=52/spec/ext/body_hash/1.0/oauth-bodyhash.html#when_to_include>
]REFS]

* OAuth 1.0 トークン要求

[8] [[クライアント]]は、[[資源所有者認可]]の後、
[DFN[[RUBYB[[[トークン要求]]]@en[token request]]]]により[[トークンcredentials]]を[[鯖]]から得ることができます。

[9] [[トークン要求]]は、[[クライアントcredentials]]と
([[トークンcredentials]]のかわりに) [[一時credentials]]を使って[[認証された要求]]です 
[SRC[>>7]]。

;; [CODE(URI)@en[[[oauth_token]]]] は[[一時credentials]] の[[識別子]]となります。

[10] [[トークン要求]]は、原則として[[要求メソッド]] [CODE(HTTP)@en[[[POST]]]]
を使います [SRC[>>7]]。[[鯖]]が何らかの方法で他の[[要求メソッド]]を[[広告]]した場合には、
そちらを使うこともできます [SRC[>>7]]。

[11] [[トークン要求]]は、[[鯖]]が何らかの方法で[[広告]]した[[トークン要求]]の[[エンドポイント]]を[[要求URL]]として使います [SRC[>>7]]。

;; [105] この[[エンドポイント]]ないし [[URL]] に [[OAuth 1.0]]
は特に名前を与えていませんでしたが、 [[OAuth 2.0]] の[[トークンエンドポイント]]に相当します。

[13] [[鯖]]は、[[トークン要求]]で[[保安輸送路]]を使わなければ[['''なりません''']]
[SRC[>>7]]。

[12] [[クライアント]]は、[[資源所有者認可]]の結果得られた[[検証符号]]
(を[[資源所有者]]経由で知ったもの) を [CODE(URI)@en[[[oauth_verifier]]]]
[[引数]]に指定しなければ[['''なりません''']] [SRC[>>7]]。

[132] [[Twitter]] [[xAuth]] はそのかわりに
[CODE(URI)@en[[[x_auth_password]]]],
[CODE(URI)@en[[[x_auth_username]]]],
[CODE(URI)@en[[[x_auth_mode]]]] の指定を求めています [SRC[>>131]]。

[138] [[OAuth Session Extension]] はそのかわりに
[CODE(URI)@en[[[oauth_session_handle]]]] を指定し、また[[一時credentials]]のかわりに現行の[[アクセストークン]]を使うことで、
[[アクセストークン]]の更新ができるとしています [SRC[>>137]]。

;; [139] 後の [[OAuth 2.0]] の[[更新トークン]]に相当する機能です。

[171] [[クライアント]]は [CODE(HTTP)@en[[[oauth_body_hash]]]]
[[引数]]を指定するべき[['''ではありません''']] [SRC[>>170]]。

[REFS[
- [131] [CITE[POST oauth/access_token | Twitter Developers]] ([TIME[2015-03-05 11:38:49 +09:00]] 版) <https://dev.twitter.com/oauth/reference/post/oauth/access_token>
- [137] [CITE@en[Implementers' Draft: OAuth Session 1.0 Draft 1]] ([TIME[2008-08-22 09:10:13 +09:00]] 版) <http://oauth.googlecode.com/svn/spec/ext/session/1.0/drafts/1/spec.html#update_access_token>
]REFS]

[14] [[鯖]]は、[[トークン要求]]を受信したら次の点を確認しなければ[['''なりません''']]
[SRC[>>7]]。
[FIG(list)[
- [15] [[要求]]が妥当であるか[[検証]]する
- [16] [[資源所有者]]が[[クライアント]]に[[トークンcredentials]]を与えることを[[認可]]していることを確認する
- [17] [[一時credentials]]が[[満期]]となっておらず、使用済みでもないか確認する
- [18] [[検証符号]]が正しいものか確認する
]FIG]

[19] 確認できたなら、[[状態符号]] [CODE(HTTP)[[[200]]]]、
[[MIME型]] [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]]
の [[payload body]] の[[応答]]とします [SRC[>>7]]。

;; [195] [[Twitter]] の[[鯖]]は [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]]
を使いながらも、 [CODE(HTTP)@en[[[Content-Type:]]]] には [CODE(MIME)@en[[[text/html]]]]
を指定します。このため[[クライアント]]は [CODE(HTTP)@en[[[Content-Type:]]]]
[[ヘッダー]]を無視しなければなりません。 [TIME[2013-09-14T08:16:00.100Z]]

[20] [[payload body]] には次の[[引数]]を含めなければ[['''なりません''']] [SRC[>>7]]。
[FIG(list members)[
[FIGCAPTION[
[[payload body]] ([CODE(MIME)@en[[[application/x-www-form-urlencoded]]]])
]FIGCAPTION]
:[CODE(URI)@en[[[oauth_token]]]]:[[トークン識別子]]
:[CODE(URI)@en[[[oauth_token_secret]]]]:[[トークン共有秘密]]
]FIG]

[136] [[OAuth Session Extension]] は更に次の[[引数]]を追加しています。
[FIG(short list)[
- [CODE(HTTP)@en[[[oauth_session_handle]]]]
- [CODE(HTTP)@en[[[oauth_authorization_expires_in]]]]
- [CODE(HTTP)@en[[[oauth_expires_in]]]]
]FIG]

[192] [[Twitter]] は次の[[引数]]を追加しています。
[FIG(short list)[
- [CODE[[[user_id]]]]
- [CODE[[[screen_name]]]]
]FIG]

[177] [[Flickr]] は次の[[引数]]を追加しています [SRC[>>176]]。
[FIG(short list)[
- [CODE(URI)@en[[[fullname]]]]
- [CODE(URI)@en[[[user_nsid]]]]
- [CODE(URI)@en[[[username]]]]
]FIG]

[169] [[はてな]]は次の[[引数]]を追加しています [SRC[>>168]]。
[FIG(short list)[
- [CODE(URI)@en[[[display_name]]]]
- [CODE(URI)@en[[[url_name]]]]
]FIG]

[184] [[Dropbox]] は [CODE[[[uid]]]] [[引数]]を追加しています [SRC[>>183]]。

[187] [[Evernote]] は [CODE[[[edam_noteStoreUrl]]]] と [CODE[[[edam_userId]]]]
と [CODE[[[edam_expires]]]] を追加しています [SRC[>>186]]。

[REFS[
- [176] [CITE@en-us[Flickr Services]] ([TIME[2015-03-06 01:25:01 +09:00]] 版) <https://www.flickr.com/services/api/auth.oauth.html>
- [168] [CITE[Consumer key を取得して OAuth 開発をはじめよう - Hatena Developer Center]] ([TIME[2015-03-05 19:09:33 +09:00]] 版) <http://developer.hatena.ne.jp/ja/documents/auth/apis/oauth/consumer>
- [183] [CITE@en[Dropbox - Core API - endpoint reference]] ([TIME[2015-03-06 09:07:10 +09:00]] 版) <https://www.dropbox.com/developers/core/docs>
- [186] [CITE@en[認証 - Evernote Developers]] ([TIME[2015-03-06 09:30:38 +09:00]] 版) <https://dev.evernote.com/intl/jp/doc/articles/authentication.php>
]REFS]

[135] [[OAuth 1.0]] 本体仕様としてはエラー時の応答については規定していません。
[[OAuth Problem Reporting Extension]] は報告方法を規定しており、
実装によってはこれに対応しています。

* OAuth 2.0 トークンエンドポイント

[35] [[認可鯖]]の[DFN[[RUBYB[[[トークンエンドポイント]]]@en[token endpoint]]]]は、
[[クライアント]]が[[認可承諾]]や[[更新トークン]]を使って[[アクセストークン]]を得るために使うものです。
[[トークンエンドポイント]]は、
[[暗示的承諾型]]以外の[[承諾型]] ([[認可符号]]、[[資源所有者合言葉credentials]]、
[[クライアントcredentials]]) で使います。 [SRC[>>34]]

** トークンエンドポイントへの要求

[36] [[クライアント]]が[[トークンエンドポイント]]の位置を知る方法は [[OAuth]]
の仕様の範囲外とされていますが、普通は[[サービス]]の[[ドキュメント]]で示されます [SRC[>>34]]。

[38] [[トークンエンドポイント]]は [[TLS]] を使わなければ[['''なりません''']] [SRC[>>34, >>89]]。
[[アクセストークン]]、[[更新トークン]]を転送する場合は [[HTTPS鯖認証]]を使わなければ[['''なりません''']] [SRC[>>89]]。
[[クライアント]]は [[RFC 6125]] により[[認可鯖]]の[[TLS証明書]]を検証し[[鯖]]の
[[identity]] を[[認証]]しなければ[['''なりません''']] [SRC[>>89]]。

[37] [[トークンエンドポイント]]の [[URL]] は 
[CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] 形式の [[query]]
を含んでも構いません。[[素片識別子]]を含んでは[['''なりません''']]。 
[[query]] が含まれる場合、[[引数]]を追加するときにはそのまま残さなければ[['''なりません''']]。
[SRC[>>34]]

[199] [[feedly]] は [CODE(HTTP)@en[[[Content-Type:]] [[application/json]]]]
を指定することで [[JSON]] [[payload body]] によって[[引数]]を指定することを認めています
[SRC[>>197]]。

[48] [[クライアント]]は、[[認可符号]]から[[アクセストークン]]を得るために[[トークンエンドポイント]]に[[要求]]を送信する場合、
[CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] [[payload body]]
により次の[[引数]]を指定します [SRC[>>52]]。
[FIG(list members)[
[FIGCAPTION[
[[payload body]] ([CODE(MIME)@en[[[application/x-www-form-urlencoded]]]])
]FIGCAPTION]
:[CODE(URI)@en[[[grant_type]]]]:[CODE(URI)@en[[[authorization_code]]]]
でなければ[['''なりません''']] [SRC[>>52]]。
:[CODE(URI)@en[[[code]]]]:受信した[[認可符号]]を指定しなければ[['''なりません''']] [SRC[>>52]]。
:[CODE(URI)@en[[[redirect_uri]]]]:[[認可エンドポイント]]への要求で
[CODE(URI)@en[[[redirect_uri]]]] [[引数]]を指定した場合は、同じ値を指定しなければ[['''なりません''']]
[SRC[>>52]]。
:[CODE(URI)@en[[[client_id]]]]:[[クライアント認証]]しない場合は指定しなければ[['''なりません''']] [SRC[>>52, >>34]]。
:[CODE(URI)@en[[[code_verifier]]]]: [[PKCE]]
]FIG]

[120] [[Azure]] は [CODE(URI)@en[[[resource]]]] [[引数]]を追加しています [SRC[>>119]]。

[150] [[Salesforce]] は [CODE(URI)@en[[[code_verifier]]]], [CODE(URI)@en[[[format]]]]
を追加しています [SRC[>>149]]。

[REFS[
- [119] [CITE@en[Authorization Code Grant Flow]] ([TIME[2015-03-05 10:35:42 +09:00]] 版) <https://msdn.microsoft.com/en-us/library/azure/dn645542.aspx>
- [149] [CITE[Understanding the Web Server OAuth Authentication Flow]] ([TIME[2015-02-28 04:30:54 +09:00]] 版) <https://www.salesforce.com/us/developer/docs/api_rest/Content/intro_understanding_web_server_oauth_flow.htm>
]REFS]

[50] [[クライアント]]は、[[資源所有者]]の [[credentials]] から[[アクセストークン]]を得るため
([[資源所有者合言葉credentials]]フロー) に[[トークンエンドポイント]]に[[要求]]を送信する場合、
[CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] [[payload body]]
により次の[[引数]]を指定します [SRC[>>61]]。
[FIG(list members)[
[FIGCAPTION[
[[payload body]] ([CODE(MIME)@en[[[application/x-www-form-urlencoded]]]])
]FIGCAPTION]
:[CODE(URI)@en[[[grant_type]]]]:[CODE(URI)@en[[[password]]]]
でなければ[['''なりません''']] [SRC[>>61]]。
:[CODE(URI)@en[[[username]]]]:[[資源所有者]]の[[利用者名]]を指定しなければ[['''なりません''']] [SRC[>>61]]。
:[CODE(URI)@en[[[password]]]]:[[資源所有者]]の[[合言葉]]を指定しなければ[['''なりません''']]
[SRC[>>61]]。
:[CODE(URI)@en[[[scope]]]]:アクセス要求の[[適用範囲]]を指定できます [SRC[>>61]]。
]FIG]

[66] [[クライアント]]は、[[クライアントcredentials]] から[[アクセストークン]]を得るため
([[クライアントcredentials]]フロー) に[[トークンエンドポイント]]に[[要求]]を送信する場合、
[CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] [[payload body]]
により次の[[引数]]を指定します [SRC[>>65]]。
[FIG(list members)[
[FIGCAPTION[
[[payload body]] ([CODE(MIME)@en[[[application/x-www-form-urlencoded]]]])
]FIGCAPTION]
:[CODE(URI)@en[[[grant_type]]]]:[CODE(URI)@en[[[client_credentials]]]]
でなければ[['''なりません''']] [SRC[>>65]]。
:[CODE(URI)@en[[[scope]]]]:アクセス要求の[[適用範囲]]を指定できます [SRC[>>65]]。
]FIG]

[125] [[Azure]] は [CODE(URI)@en[[[resource]]]] [[引数]]を追加しています [SRC[>>124]]。

[146] [[reddit]] は場合によって [CODE(URI)@en[[[grant_type]]=[[https://oauth.reddit.com/grants/installed_client]]]]
を使います。また [CODE(URI)@en[[[device_id]]]] [[引数]]を追加しています。 [SRC[>>145]]

[REFS[
- [124] [CITE@en[Service to Service Calls Using Client Credentials]] ([TIME[2015-03-05 10:43:44 +09:00]] 版) <https://msdn.microsoft.com/en-us/library/azure/dn645543.aspx>
- [145] [CITE@en[OAuth2 · reddit/reddit Wiki]] ([TIME[2015-03-05 16:39:22 +09:00]] 版) <https://github.com/reddit/reddit/wiki/OAuth2#user-content-application-only-oauth>
]REFS]

[2] [[クライアント]]は、[[更新トークン]] から[[アクセストークン]]を得るために[[トークンエンドポイント]]に[[要求]]を送信する場合、
[CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] [[payload body]]
により次の[[引数]]を指定します [SRC[>>1]]。
[FIG(list members)[
[FIGCAPTION[
[[payload body]] ([CODE(MIME)@en[[[application/x-www-form-urlencoded]]]])
]FIGCAPTION]
:[CODE(URI)@en[[[grant_type]]]]:[CODE(URI)@en[[[refresh_token]]]]
でなければ[['''なりません''']] [SRC[>>1]]。
:[CODE(URI)@en[[[refresh_token]]]]:[[クライアント]]に発行されている[[更新トークン]]を指定しなければ[['''なりません''']] [SRC[>>1]]。
:[CODE(URI)@en[[[scope]]]]:アクセス要求の[[適用範囲]]を指定できます [SRC[>>1]]。
元々[[資源所有者]]に[[承諾]]された[[適用範囲]]以外を指定しては[['''なりません''']] [SRC[>>1]]。
省略すると、元々[[資源所有者]]に[[承諾]]された[[適用範囲]]を表します [SRC[>>1]]。
]FIG]

[122] [[Azure]] は [CODE(URI)@en[[[resource]]]] [[引数]]を追加しています [SRC[>>119]]。

[158] [[Salesfoce]] は [CODE(URI)@en[[[format]]]] [[引数]]を追加しています [SRC[>>157]]。

[203] [[assertion]] を使う場合は、 [CODE(URI)@en[[[grant_type]]]]
[[引数]]を [[assertion]] の種別を表す[[絶対URL]]、 [CODE(URI)@en[[[assertion]]]] 
[[引数]]を [[assertion]] の値とします。 [CODE(URI)@en[[[scope]]]]
[[引数]]も指定できます。 [SRC[>>202]]

[EG[
[70] 例えば [[SAML 2.0]] が拡張の[[承諾型]]を規定しています。
]EG]

[EG[
[113] 例えば [[JWT]] を使う場合、
[CODE(URI)@en[[[grant_type]]=[[urn:ietf:params:oauth:grant-type:jwt-bearer]]]]
と [CODE(URI)@en[[[assertion]]]] [[引数]]を指定します。

[114] [[Google]] がこれを実装している [SRC[>>115]] ようです。
[REFS[
- [115] [CITE@ja[Using OAuth 2.0 for Server to Server Applications - Google Accounts Authentication and Authorization — Google Developers]] ([TIME[2015-02-04 03:07:26 +09:00]] 版) <https://developers.google.com/accounts/docs/OAuth2ServiceAccount>
]REFS]
]EG]

[69] [[クライアント]]は、拡張の[[承諾型]]の規定に従い[[トークンエンドポイント]]に[[要求]]を送信する場合、
[CODE(URI)@en[[[grant_type]]]] [[引数]]に[[絶対URL]]を指定し、
必要に応じて追加の[[引数]]も指定します [SRC[>>68]]。

[EG[
[116] [[Google]] は [[OAuth 1.0]] から [[OAuth 2.0]] への移行のために
[CODE(URI)@en[[[grant_type]]=[[urn:ietf:params:oauth:grant-type:migration:oauth1]]]]
を提供していました。 [[OAuth 2.0]] [[クライアント認証]]、 [[OAuth 1.0]] による
[[HTTP認証]]、省略可能な [CODE(URI)@en[[[scope]]]] [[引数]]を指定することで、
[[更新トークン]]を得ることができるものです。

;; [CODE(URI)@en[[[urn:ietf:params:oauth:grant-type:migration:oauth1]]]] 参照。
]EG]

[EG[
[165] [[Facebook]] の [CODE(URI)@en[[[grant_type]]=[[fb_exchange_token]]]]
は [CODE(URI)@en[[[fb_exchange_token]]]] [[引数]]を使っています [SRC[>>164]]。
]EG]

;; [123] [[Azure]] は [CODE(URI)@en[[[grant_type]]]] という名前で
[CODE(URI)@en[[[response_type]]]] [[引数]]に相当するものを指定させています [SRC[>>119, >>124]]。

[41] [[要求]]の[[引数]]は、複数指定しては[['''なりません''']] [SRC[>>34]]。

[160] [[SurveyMonkey]] は [[payload body]] ではなく [[URL query]] に
[CODE(URI)@en[[[api_key]]]] [[引数]]を指定することを求めています [SRC[>>159]]。

[189] [[GitLab]] は[[要求]]の [[payload body]] の [[JSON]] によって[[引数]]を指定させています [SRC[>>188]]。

[REFS[
- [159] [CITE@en[SurveyMonkey - Guide OAuth]] ([TIME[2015-03-05 17:23:37 +09:00]] 版) <https://developer.surveymonkey.com/mashery/guide_oauth>
- [161] [CITE[Connecting]] ([TIME[2015-03-05 17:39:49 +09:00]] 版) <https://developer.foursquare.com/overview/auth>
- [164] [CITE@ja[Access Tokens]] ([TIME[2015-03-05 18:03:06 +09:00]] 版) <https://developers.facebook.com/docs/facebook-login/access-tokens>
- [188] [CITE@en[gitlabhq/oauth2.md at master · gitlabhq/gitlabhq]] ([TIME[2015-03-06 10:46:37 +09:00]] 版) <https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/oauth2.md>
]REFS]

[39] [[クライアント]]は[[トークンエンドポイント]]への[[要求]]で [CODE(HTTP)@en[[[POST]]]]
[[要求メソッド]]を使わなければ[['''なりません''']] [SRC[>>34]]。

[162] [[Facebook]] [SRC[>>164]] や [[fouresquare]] [SRC[>>161]]
は [CODE(HTTP)@en[[[GET]]]] を使っているようです。

** クライアント認証

[22] [[クライアント]]は、[[トークンエンドポイント]]や
[[revokeエンドポイント]]への[[要求]]で、
[CODE(URI)@en[[[client_id]]]] [[引数]]を指定できます。また、
場合によっては[[認証]]に必要な情報を指定しなければなりません。

[23] [[認可サーバー]]は、[[トークンエンドポイント]]や[[revokeエンドポイント]]で、
場合によっては[[クライアント認証]]を行わなければなりません。

[24] 詳細は、[[OAuth 2.0クライアント認証]]を参照。

;; [25] [[TLSクライアント認証]]とは関係ありません。

** トークンエンドポイントの処理

[83] [[認可鯖]]は、認識できない[[引数]]を無視しなければ[['''なりません''']] [SRC[>>34]]。

[40] [[要求]]の[[引数]]に値が含まれなければ、指定されなかったものと扱わなければ[['''なりません''']] [SRC[>>34]]。

;; [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]] で [CODE[[[=]]]]
が含まれない場合を指しているのでしょうか? [[空文字列]]が指定された場合と区別する必要があるということでしょうか??

[54] [[認可鯖]]は、[[認可符号]]から[[アクセストークン]]の発行を求められている場合、
[[認可符号]]が妥当なものであることを確認しなければ[['''なりません''']] [SRC[>>52]]。
[[認可符号]]は1回しか使えません。

[100] 攻撃者は大量の[[要求]]で[[クライアント]]に適当な[[認可符号]]を与え、[[認可鯖]]にアクセスさせることで、
間接的に[[認可鯖]]の[[HTTP接続]]を枯渇させる [[DoS攻撃]]を試みるかもしれません。
[[認可鯖]]には攻撃者の情報がほとんど届かず、対策が難しい攻撃です。
[[認可鯖]]は不適切な[[認可符号]]があまりに多い[[クライアント]]には[[誤り]]応答を返すべきかもしれません [SRC[>>99]]。

[REFS[
- [99] [CITE@en[RFC 6819 - OAuth 2.0 Threat Model and Security Considerations]] ([TIME[2015-02-10 06:43:00 +09:00]] 版) <http://tools.ietf.org/html/rfc6819#section-4.4.1.12>
]REFS]

[53] [[認可鯖]]は、[[認可符号]]から[[アクセストークン]]の発行を求められている場合、

- [32] [[クライアント型]]が[[機密]]の[[クライアント]]に対するものであるか、
- [33] [[クライアント型]]が[[公開]]の[[クライアント]]に対するもので[[認可符号]]の発行先と
[CODE(URI)@en[[[client_id]]]] が合致しているか、

のいずれかを満たすことを確認しなくては[MUST[なりません]] [SRC[>>52, >>89]]。

[102] 攻撃対象の[[クライアント]]がいわゆる[[OAuthログイン]]を実装している場合において、攻撃者の有する[[クライアント]]に攻撃対象の[[資源所有者]]を誘導して[[認可符号]]を取得し、その[[認可符号]]を攻撃対象の[[クライアント]]に与えることで、攻撃者が攻撃対象[[クライアント]]における攻撃対象[[資源所有者]]になりすまして[[ログイン]]できてしまいます [SRC[>>101]]。
[[公開]]の[[クライアント]]に関してこれを防ぐことはできません。

[NOTE[
[43] 
つまり実行環境非公開の[[ウェブサーバー]]内部で[[トークンエンドポイント]]を呼び出す
[[Webサービス]]のような利用形態なら、[[認可鯖]]が適切に検査していれば、
[[クライアント]]の [[credentials]] が流出しない限りにおいて、
たとえ[[認可符号]]が流出したとしても攻撃は防げます。
ところが一般配布される[[スマートフォンアプリ]]のような利用形態だと、
[[クライアント]]の [[credentials]] は攻撃者も入手可能な状態にあるので、
[[認可符号]]の流出は致命的な問題になります。

[44] 
流出の可能性として考えられるのは、

- [45] 関係する[[Webアプリケーション]]に [[XSS脆弱性]]、
サーバーへの侵入など深刻な[[セキュリティー]]上の問題が発生している時
- [46] [[認可鯖]]の[[リダイレクト]]先に本来の[[クライアント]]の [[URL]]
以外の攻撃者のサーバーが指定できるなど、
[[OAuth]] の実装に深刻な[[セキュリティー]]上の問題が存在する時
- [47] 関係する[[Webアプリケーション]]が[[素のHTTP]]
を使っている、[[利用者]]が [[MITM proxy]] を使っているなど、
[[通信路]]に深刻な[[セキュリティー]]上の問題が発生している時
- [57] 
[[利用者]]の [[Webブラウザー]]が悪意ある提供者によるもの
(いわゆる[[アプリ内ブラウザー]]など)
など、
利用環境に深刻な[[セキュリティー]]上の問題が発生している時
- [49] [[認可鯖]]の[[リダイレクト]]先に [[HTTP(S) URL]] 以外の一般的でない
[[URL scheme]] ([[スマホアプリ]]のいわゆる[[カスタムURL scheme]]など [SEE[ [[URL Scheme Hijacking]] ]])
のような[[セキュリティー]]に問題があるものが使われている時

などでしょうか。


]NOTE]

[REFS[
- [101] [CITE@en[RFC 6819 - OAuth 2.0 Threat Model and Security Considerations]] ([TIME[2015-02-10 06:43:00 +09:00]] 版) <http://tools.ietf.org/html/rfc6819#section-4.4.1.13>
]REFS]

[54] [[認可鯖]]は、[[認可符号]]から[[アクセストークン]]の発行を求められている場合、
[[認可エンドポイント]]へのアクセス時に [CODE(URI)@en[[[redirect_uri]]]]
[[引数]]が指定されていたなら本要求にも [CODE(URI)@en[[[redirect_uri]]]]
[[引数]]が含まれており、両者の値が一致することを確認しなければ[['''なりません''']]
[SRC[>>52, >>89]]。

[62] [[認可鯖]]は、[[資源所有者]]の [[credentials]] から[[アクセストークン]]の発行を求められている場合、
指定された [[credentials]] を検証しなければ[['''なりません''']] [SRC[>>52]]。

[3] [[認可鯖]]は、[[更新トークン]]から[[アクセストークン]]の発行を求められている場合、
指定された[[更新トークン]]を検証しなければ[['''なりません''']] [SRC[>>1]]。
[[クライアント認証]]可能であれば、正しい[[クライアント]]か検証しなければ[['''なりません''']]
[SRC[>>89]]。

[63] [[認可鯖]]は、回数制限や警告などにより、[[資源所有者合言葉credentials]]
の[[総当たり攻撃]]から[[エンドポイント]]を保護しなければ[['''なりません''']] [SRC[>>52]]。

[95] [[認可鯖]]は [[CSRF]] 対策が必要です。[[資源所有者]]の [[credentials]]
を第三者に漏らしてしまう形の攻撃は難しそうですが、[[認可鯖]] (や間接的に[[クライアント]])
に対する [[DoS攻撃]]や、 [[CSRF]] を引き起こされた被害者たる
[[Webブラウザー]]の利用者への嫌がらせには使えるかもしれません。

;; [96] その性質上、[[CSRF]] 対策用のトークンを指定させるような方法は使えません。
[CODE(HTTP)@en[[[Origin:]]]] [[ヘッダー]]の値と[[クライアント]]の登録情報を比較して不適切な[[要求]]を検出するのがよいでしょうか。

** トークンエンドポイントの応答

[56] [[認可鯖]]は、[[アクセストークン]]の発行を求められている場合、
要求が妥当であり[[認可]]されたなら、[[アクセストークン]]を発行します
[SRC[>>55, >>64, >>67, >>68, >>1]]。

[4] [[認可符号]]や[[資源所有者合言葉credentials]]、拡張の[[承諾型]]では、
[[更新トークン]]も発行することもできます [SRC[>>55, >>64, >>68]]。 
ただし[[クライアント認証]]していない場合には発行するべきではないかもしれません [SRC[>>89]]。

[5] [[クライアントcredentials]]では、
[[更新トークン]]を発行する[['''べきではありません''']] [SRC[>>67]]。

[6] [[更新トークン]]から[[アクセストークン]]を求めている場合には、
新しい[[更新トークン]]を発行しても構いません。その[[適用範囲]]
([[scope]]) は元の[[更新トークン]]と同じでなければ[['''なりません''']]。
その場合には[[認可鯖]]は古い[[更新トークン]]を取り消し ([[revoke]])
して構いませんし、[[クライアント]]は古い[[更新トークン]]を破棄しなければ[['''なりません''']]。
[SRC[>>1]]

[72] その場合には、 [CODE(HTTP)[[[200]]]] [[応答]]で次の[[引数]]を
[[JSON]] ([CODE(MIME)@en[[[application/json]]]]) [[payload body]] の 
[[JSONオブジェクト]]の名前と値 ([[文字列]]なら [[JSON文字列]]、
[[数値]]なら [[JSON数値]]) に含めます [SRC[>>71]]。
[FIG(list members)[
[FIGCAPTION[
[[payload body]] ([[JSONオブジェクト]])
]FIGCAPTION]
:[73] [CODE(HTTP)@en[[[access_token]]]]:[[認可鯖]]が発行した[[アクセストークン]]を指定しなければ[['''なりません''']] [SRC[>>71]]。
:[74] [CODE(HTTP)@en[[[token_type]]]]:発行した[[トークン]]の種類を指定しなければ[['''なりません''']]
[SRC[>>71]]。
:[75] [CODE(HTTP)@en[[[expires_in]]]]:[[アクセストークン]]の[[寿命]]を[[秒]]単位で指定する[['''べきです''']] [SRC[>>71]]。
:[76] [CODE(HTTP)@en[[[refresh_token]]]]:同じ[[認可承諾]]を使って新しい[[アクセストークン]]を得られる[[更新トークン]]を指定できます [SRC[>>71]]。
:[77] [CODE(HTTP)@en[[[scope]]]]:[[クライアント]]が要求した[[適用範囲]]と[[アクセストークン]]の[[適用範囲]]が異なるなら、指定しなければ[['''なりません''']]。同じ場合も指定できます。 [SRC[>>71]]
:[107] [CODE(HTTP)@en[[[id_token]]]]:[[OpenID Connect]] の場合には指定しなければ[['''なりません''']] [SRC[>>108]]。
]FIG]

;; [78] どれが文字列でどれが数値かは明確ではありませんが、
[CODE(HTTP)@en[[[expires_in]]]] は[[数値]]、それ以外は[[文字列]]と思われます。

[106] [[アクセストークン型]]によっては、更に追加の[[引数]]を指定する必要があるかもしれません。

;; [[アクセストークン型]]を参照。

[117] [CODE(URI)@en[[[grant_type]]=[[urn:ietf:params:oauth:grant-type:migration:oauth1]]]]
では、 [CODE(URI)@en[[[refresh_token]]]] ([[更新トークン]]) のみが返されることになっていました。

[121] [[Azure]] は [CODE(URI)@en[[[resource]]]] [[引数]]と
[CODE(URI)@en[[[expires_on]]]] [[引数]]を追加しています [SRC[>>119]]。

[134] [[ヤマレコ]]は [CODE(URI)@en[[[token_type]]]] [[引数]]を指定せず、
成功時でも [CODE[[[error]]]] [[引数]]と [CODE[[[error_message]]]]
[[引数]] (独自) を指定するようです [SRC[>>133]]。

[148] [[Instagram]] は [CODE(URI)@en[[[token_type]]]] [[引数]]を指定せず、
[CODE(URI)@en[[[user]]]] [[引数]] (独自) を指定するようです [SRC[>>147]]。

;; [CODE(URI)@en[[[user]]]] の値は [[JSONオブジェクト]]です。

[173] [[Bitly]] は [CODE@en[[[apiKey]]]] [[引数]] (独自) を指定するようです。
ただし非推奨で削除予定とあります。 [SRC[>>172]] [TIME[2015-03-05T16:02:49.500Z]]

[179] [[Heroku]] は [CODE@en[[[user_id]]]]、[CODE[[[session_nonce]]]]
を指定するようです [SRC[>>178]]。

[182] [[Yahoo!]] は [CODE@en[[[xoauth_yahoo_guid]]]] を指定するようです [SRC[>>181]]。

[198] [[feedly]] は [CODE[[[plan]]]] を指定するようです [SRC[>>197]]。

[143] [[foursquare]] など [SRC[>>161, >>163, >>142, >>172]] は [CODE(URI)@en[[[token_type]]]] [[引数]]を指定しないようです。

[194] [[Facebook]] は [CODE(URI)@en[[[token_type]]]] [[引数]]を指定しません。
[CODE(URI)@en[[[expires_in]]]] ではなく [CODE(URI)@en[[[expires]]]]
[[引数]]に [[time_t]] 値を指定するようです。

[152] [[Salesforce]] は [CODE(URI)@en[[[token_type]]]] [[引数]]を指定しないようです。
独自の [CODE(URI)@en[[[id]]]], [CODE(URI)@en[[[instance_url]]]],
[CODE(URI)@en[[[issued_at]]]], [CODE(URI)@en[[[signature]]]] を指定するようです。 [SRC[>>149, >>156, >>157]]

[151] [[Salesforce]] は[[要求]]で [CODE(URI)@en[[[format]]]] [[引数]]を指定した場合に[[応答]]が他の[[MIME型]]になるようです [SRC[>>149, >>157]]。

[167] [[GitHub]] は[[要求]]で [CODE(HTTP)@en[[[Accept:]]]] を指定した場合に[[応答]]が他の
[[MIME型]]になるようです。既定値は [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]]
になっているようです。 [SRC[>>166]]

[193] [[Facebook]] は[[応答]]に [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]]
を使うようです。ただし [CODE(HTTP)@en[[[Content-Type:]]]] はなぜか
[CODE[[[text/plain]]; [[charset]]=[[UTF-8]]]] になります。 [TIME[2015-03-10T15:02:45.900Z]]

[30] その後 [[JSON]] に修正 (非互換変更) されています [SRC[>>29]]。

-*-*-

[42] [[応答]]の[[引数]]は、複数指定しては[['''なりません''']] [SRC[>>34]]。

[79] 更に、 [CODE(HTTP)@en[[[Cache-Control:]] [[no-store]]]] と
[CODE(HTTP)@en[[[Pragma:]] [[no-cache]]]] を含めなければ[['''なりません''']] [SRC[>>71]]。

[80] [[クライアント]]は、認識できない名前の[[引数]]を無視しなければ[['''なりません''']]
[SRC[>>71]]。

-*-*-

[56] [[認可鯖]]は、[[アクセストークン]]の発行を求められている場合、
[[クライアント認証]]に失敗したか要求が非妥当であったなら、
誤り応答を返します [SRC[>>55, >>64, >>67, >>68, >>1]]。

[88] その場合には、 [CODE(HTTP)[[[400]]]] [[応答]]で次の[[引数]]を
[[JSON]] ([CODE(MIME)@en[[[application/json]]]]) [[payload body]] の 
[[JSONオブジェクト]]の名前と値 ([[文字列]]なら [[JSON文字列]]、
[[数値]]なら [[JSON数値]]) に含めます [SRC[>>87]]。
[FIG(list members)[
[FIGCAPTION[
[[payload body]] ([[JSONオブジェクト]])
]FIGCAPTION]
:[84] [CODE(URI)@en[[[error]]]]:[[誤り符号]]を指定しなければ[['''なりません''']] [SRC[>>87]]。
:[85] [CODE(URI)@en[[[error_description]]]]:[[人間可読]]な[[誤り]]の説明を指定して構いません
[SRC[>>87]]。
:[86] [CODE(URI)@en[[[error_uri]]]]:[[人間可読]]な[[誤り]]の説明を含む[[Webページ]]の [[URL]]
を指定して構いません [SRC[>>87]]。
]FIG]

;; [126] [[誤り符号]]によっては、他の [[HTTP]] [[状態符号]]を使うこともあるようです。

[127] [[Azure]] は [CODE(URI)@en[[[timestamp]]]]、[CODE(URI)@en[[[trace_id]]]]、
[CODE@en[[[correlation_id]]]]、[CODE@en[[[error_codes]]]] を追加しています [SRC[>>128]]。

[REFS[
- [166] [CITE@en[OAuth | GitHub API]] ([TIME[2015-03-05 18:02:01 +09:00]] 版) <https://developer.github.com/v3/oauth/>
- [128] [CITE@en[Token Issuance Endpoint Errors]] ([TIME[2015-03-05 10:47:08 +09:00]] 版) <https://msdn.microsoft.com/en-us/library/azure/dn645548.aspx>
- [133] [CITE[1. OAuth認証 - ヤマレコ Web API]] ([TIME[2015-02-25 16:41:06 +09:00]] 版) <https://sites.google.com/site/apiforyamareco/api/oauth>
- [142] [CITE@en[OAuth | Slack]] ([[Slack]] 著, [TIME[2015-03-05 16:04:09 +09:00]] 版) <https://api.slack.com/docs/oauth>
- [147] [CITE@ja[• Instagram Developer Documentation]] ([TIME[2015-03-05 16:42:50 +09:00]] 版) <https://instagram.com/developer/authentication/>
- [156] [CITE[Understanding the Username-Password OAuth Authentication Flow]] ([TIME[2015-02-28 04:30:54 +09:00]] 版) <https://www.salesforce.com/us/developer/docs/api_rest/Content/intro_understanding_username_password_oauth_flow.htm>
- [157] [CITE[Understanding the OAuth Refresh Token Process]] ([TIME[2015-02-28 04:30:54 +09:00]] 版) <https://www.salesforce.com/us/developer/docs/api_rest/Content/intro_understanding_refresh_token_oauth.htm>
- [163] [CITE@en[Authenticating with OAuth 2.0 | LinkedIn Developer Network]] ([TIME[2015-03-05 17:56:22 +09:00]] 版) <https://developer.linkedin.com/docs/oauth2>
- [172] [CITE[Bitly API Documentation]] ([TIME[2014-11-01 00:33:47 +09:00]] 版) <http://dev.bitly.com/authentication.html>
- [178] [CITE@en[OAuth | Heroku Dev Center]] ([TIME[2015-03-06 08:03:38 +09:00]] 版) <https://devcenter.heroku.com/articles/oauth#oauth-flow-response>
- [181] [CITE[Yahoo OAuth 2.0 Guide - Yahoo Developer Network]] ([TIME[2015-03-06 08:52:24 +09:00]] 版) <https://developer.yahoo.com/oauth2/guide/>
- [197] [CITE@en[Authentication | feedly Cloud API]] ([TIME[2015-01-23 08:10:58 +09:00]] 版) <https://developer.feedly.com/v3/auth/>
]REFS]

[51] [CODE(URI)@en[[[error]]]] が [CODE@en[[[invalid_client]]]] の場合には、
[CODE(HTTP)[[[401]]]] [[応答]]とすることもできます。
[[クライアント]]が [CODE(HTTP)@en[[[Authorization:]]]] [[ヘッダー]]で[[認証]]を試みた場合には、
[CODE(HTTP)[[[401]]]] [[応答]]と [CODE(HTTP)@en[[[WWW-Authenticate:]]]]
[[ヘッダー]] ([[auth-scheme]] は[[要求]]で指定されたもの) を使って[[応答]]しなければ[['''なりません''']] [SRC[>>87]]。

* メモ

[196] [CITE@en[draft-richer-oauth-xml-01 - Alternate Encoding for OAuth 2 Token Responses]]
([TIME[2015-01-18 17:52:17 +09:00]] 版)
<https://tools.ietf.org/html/draft-richer-oauth-xml-01>

[FIG(quote)[
[FIGCAPTION[
[26] [CITE[Twitter Login にも CSRF 脆弱性ができやすい罠が!? - OAuth.jp]]
([[Nov Matake]]著, [TIME[2016-07-01 15:53:19 +09:00]])
<http://oauth.jp/blog/2014/06/23/csrf-on-twitter-login/>
]FIGCAPTION]

> Access Token 取得時のリクエストの署名に、Request Token Secret を使わなくてもいいんです。なので Session に紐づいた Request Token Secret が空の場合にそれをそのまま空文字列として処理してしまう RP は、正常に Access Token を受け取れてしまいます。
> つまり、攻撃者が被害者にこんな URL を踏ませると、被害者は攻撃者の Twitter アカウントを使ってあなたのサイトにログインできてしまう可能性があるのです。
> https://client.example.com/callback?
>   oauth_token=<request-token-authorized-by-attacker>&
>   oauth_verifier=<valid-oauth-verifier>

]FIG]


[FIG(quote)[
[FIGCAPTION[
[27] [CITE@en[Private-Webmention - IndieWeb]]
([TIME[2016-12-21 07:15:51 +09:00]])
<https://indieweb.org/Private-Webmention>
]FIGCAPTION]

> Link: <https://aaronpk.example/token>; rel="token_endpoint"

]FIG]


[28] [CITE@en[token-endpoint - IndieWeb]]
([TIME[2016-12-21 15:16:17 +09:00]])
<https://indieweb.org/token-endpoint>

[FIG(quote)[
[FIGCAPTION[
[29] [CITE@ja[Changelog(更新履歴) - Graph API]]
( ([TIME[2017-02-28 10:19:03 +09:00]]))
<https://developers.facebook.com/docs/apps/changelog#v2_3_changes>
]FIGCAPTION]

> access_tokenのコードを交換した際に返されるhttps://www.facebook.com/v2.3/oauth/access_tokenの応答フォーマットが、URLエンコードされるのではなく、有効なJSONを返すようになりました。 この応答の新しいフォーマットは、{"access_token": {TOKEN}, "token_type":{TYPE}, "expires_in":{TIME}}です。RFC 6749のセクション5.1に準拠するために、このアップデートを行いました。

]FIG]


[31] 
[[Slack]]
は
[[OAuth 2.0]]
対応していることになっているが、[[トークンエンドポイント]]相当は似て非なる、
互換性のない別物。
[TIME[2021-01-30T06:33:55.700Z]]