token_endpoint

トークンエンドポイント (OAuth)

[104] トークンエンドポイント (token endpoint) は、 クライアントにアクセストークンを発行する認可鯖上のエンドポイントです。

仕様書

OAuth 1.0 トークン要求

[8] クライアントは、資源所有者認可の後、 トークン要求 (token request) によりトークンcredentialsを鯖から得ることができます。

[9] トークン要求は、クライアントcredentialsと (トークンcredentialsのかわりに) 一時credentialsを使って認証された要求です >>7。

oauth_token は一時credentials の識別子となります。

[10] トークン要求は、原則として要求メソッド POST を使います >>7。鯖が何らかの方法で他の要求メソッドを広告した場合には、 そちらを使うこともできます >>7。

[11] トークン要求は、鯖が何らかの方法で広告したトークン要求のエンドポイントを要求URLとして使います >>7。

[105] このエンドポイントないし URL に OAuth 1.0 は特に名前を与えていませんでしたが、 OAuth 2.0 のトークンエンドポイントに相当します。

[13] 鯖は、トークン要求で保安輸送路を使わなければなりません >>7。

[12] クライアントは、資源所有者認可の結果得られた検証符号 (を資源所有者経由で知ったもの) を oauth_verifier 引数に指定しなければなりません >>7。

[132] Twitter xAuth はそのかわりに x_auth_password, x_auth_username, x_auth_mode の指定を求めています >>131。

[138] OAuth Session Extension はそのかわりに oauth_session_handle を指定し、また一時credentialsのかわりに現行のアクセストークンを使うことで、 アクセストークンの更新ができるとしています >>137。

[139] 後の OAuth 2.0 の更新トークンに相当する機能です。

[171] クライアントは oauth_body_hash 引数を指定するべきではありません >>170。

[14] 鯖は、トークン要求を受信したら次の点を確認しなければなりません >>7。

[19] 確認できたなら、状態符号 200、 MIME型 application/x-www-form-urlencoded の payload body の応答とします >>7。

[195] Twitter の鯖は application/x-www-form-urlencoded を使いながらも、 Content-Type: には text/html を指定します。このためクライアントは Content-Type: ヘッダーを無視しなければなりません。

[20] payload body には次の引数を含めなければなりません >>7。

payload body (application/x-www-form-urlencoded)
oauth_token
トークン識別子
oauth_token_secret
トークン共有秘密

[136] OAuth Session Extension は更に次の引数を追加しています。

[192] Twitter は次の引数を追加しています。

[177] Flickr は次の引数を追加しています >>176。

[169] はてなは次の引数を追加しています >>168。

[184] Dropbox は uid 引数を追加しています >>183。

[187] Evernote は edam_noteStoreUrl と edam_userId と edam_expires を追加しています >>186。

[135] OAuth 1.0 本体仕様としてはエラー時の応答については規定していません。 OAuth Problem Reporting Extension は報告方法を規定しており、 実装によってはこれに対応しています。

OAuth 2.0 トークンエンドポイント

[35] 認可鯖のトークンエンドポイント (token endpoint) は、 クライアントが認可承諾や更新トークンを使ってアクセストークンを得るために使うものです。 トークンエンドポイントは、 暗示的承諾型以外の承諾型 (認可符号、資源所有者合言葉credentials、 クライアントcredentials) で使います。 >>34

トークンエンドポイントへの要求

[36] クライアントがトークンエンドポイントの位置を知る方法は OAuth の仕様の範囲外とされていますが、普通はサービスのドキュメントで示されます >>34。

[38] トークンエンドポイントは TLS を使わなければなりません >>34, >>89。 アクセストークン、更新トークンを転送する場合は HTTPS鯖認証を使わなければなりません >>89。 クライアントは RFC 6125 により認可鯖のTLS証明書を検証し鯖の identity を認証しなければなりません >>89。

[37] トークンエンドポイントの URL は application/x-www-form-urlencoded 形式の query を含んでも構いません。素片識別子を含んではなりません。 query が含まれる場合、引数を追加するときにはそのまま残さなければなりません。 >>34

[199] feedly は Content-Type: application/json を指定することで JSON payload body によって引数を指定することを認めています >>197。

[48] クライアントは、認可符号からアクセストークンを得るためにトークンエンドポイントに要求を送信する場合、 application/x-www-form-urlencoded payload body により次の引数を指定します >>52。

payload body (application/x-www-form-urlencoded)
grant_type
authorization_code でなければなりません >>52。
code
受信した認可符号を指定しなければなりません >>52。
redirect_uri
認可エンドポイントへの要求で redirect_uri 引数を指定した場合は、同じ値を指定しなければなりません >>52。
client_id
クライアント認証しない場合は指定しなければなりません >>52, >>34。
code_verifier
PKCE

[120] Azure は resource 引数を追加しています >>119。

[150] Salesforce は code_verifier, format を追加しています >>149。

[50] クライアントは、資源所有者の credentials からアクセストークンを得るため (資源所有者合言葉credentialsフロー) にトークンエンドポイントに要求を送信する場合、 application/x-www-form-urlencoded payload body により次の引数を指定します >>61。

payload body (application/x-www-form-urlencoded)
grant_type
password でなければなりません >>61。
username
資源所有者の利用者名を指定しなければなりません >>61。
password
資源所有者の合言葉を指定しなければなりません >>61。
scope
アクセス要求の適用範囲を指定できます >>61。

[66] クライアントは、クライアントcredentials からアクセストークンを得るため (クライアントcredentialsフロー) にトークンエンドポイントに要求を送信する場合、 application/x-www-form-urlencoded payload body により次の引数を指定します >>65。

payload body (application/x-www-form-urlencoded)
grant_type
client_credentials でなければなりません >>65。
scope
アクセス要求の適用範囲を指定できます >>65。

[125] Azure は resource 引数を追加しています >>124。

[146] reddit は場合によって grant_type=https://oauth.reddit.com/grants/installed_client を使います。また device_id 引数を追加しています。 >>145

[2] クライアントは、更新トークン からアクセストークンを得るためにトークンエンドポイントに要求を送信する場合、 application/x-www-form-urlencoded payload body により次の引数を指定します >>1。

payload body (application/x-www-form-urlencoded)
grant_type
refresh_token でなければなりません >>1。
refresh_token
クライアントに発行されている更新トークンを指定しなければなりません >>1。
scope
アクセス要求の適用範囲を指定できます >>1。 元々資源所有者に承諾された適用範囲以外を指定してはなりません >>1。 省略すると、元々資源所有者に承諾された適用範囲を表します >>1。

[122] Azure は resource 引数を追加しています >>119。

[158] Salesfoce は format 引数を追加しています >>157。

[203] assertion を使う場合は、 grant_type 引数を assertion の種別を表す絶対URL、 assertion 引数を assertion の値とします。 scope 引数も指定できます。 >>202

[70] 例えば SAML 2.0 が拡張の承諾型を規定しています。

[69] クライアントは、拡張の承諾型の規定に従いトークンエンドポイントに要求を送信する場合、 grant_type 引数に絶対URLを指定し、 必要に応じて追加の引数も指定します >>68。

[116] Google は OAuth 1.0 から OAuth 2.0 への移行のために grant_type=urn:ietf:params:oauth:grant-type:migration:oauth1 を提供していました。 OAuth 2.0 クライアント認証、 OAuth 1.0 による HTTP認証、省略可能な scope 引数を指定することで、 更新トークンを得ることができるものです。

[123] Azure は grant_type という名前で response_type 引数に相当するものを指定させています >>119, >>124。

[41] 要求の引数は、複数指定してはなりません >>34。

[160] SurveyMonkey は payload body ではなく URL query に api_key 引数を指定することを求めています >>159。

[189] GitLab は要求の payload body の JSON によって引数を指定させています >>188。

[39] クライアントはトークンエンドポイントへの要求で POST 要求メソッドを使わなければなりません >>34。

[162] Facebook >>164 や fouresquare >>161 は GET を使っているようです。

クライアント認証

[22] クライアントは、トークンエンドポイントや revokeエンドポイントへの要求で、 client_id 引数を指定できます。また、 場合によっては認証に必要な情報を指定しなければなりません。

[23] 認可サーバーは、トークンエンドポイントやrevokeエンドポイントで、 場合によってはクライアント認証を行わなければなりません。

[24] 詳細は、OAuth 2.0クライアント認証を参照。

[25] TLSクライアント認証とは関係ありません。

トークンエンドポイントの処理

[83] 認可鯖は、認識できない引数を無視しなければなりません >>34。

[40] 要求の引数に値が含まれなければ、指定されなかったものと扱わなければなりません >>34。

application/x-www-form-urlencoded で = が含まれない場合を指しているのでしょうか? 空文字列が指定された場合と区別する必要があるということでしょうか??

[54] 認可鯖は、認可符号からアクセストークンの発行を求められている場合、 認可符号が妥当なものであることを確認しなければなりません >>52。 認可符号は1回しか使えません。

[100] 攻撃者は大量の要求でクライアントに適当な認可符号を与え、認可鯖にアクセスさせることで、 間接的に認可鯖のHTTP接続を枯渇させる DoS攻撃を試みるかもしれません。 認可鯖には攻撃者の情報がほとんど届かず、対策が難しい攻撃です。 認可鯖は不適切な認可符号があまりに多いクライアントには誤り応答を返すべきかもしれません >>99。

[53] 認可鯖は、認可符号からアクセストークンの発行を求められている場合、

のいずれかを満たすことを確認しなくてはなりません >>52, >>89。

[102] 攻撃対象のクライアントがいわゆるOAuthログインを実装している場合において、攻撃者の有するクライアントに攻撃対象の資源所有者を誘導して認可符号を取得し、その認可符号を攻撃対象のクライアントに与えることで、攻撃者が攻撃対象クライアントにおける攻撃対象資源所有者になりすましてログインできてしまいます >>101。 公開のクライアントに関してこれを防ぐことはできません。

[43] つまり実行環境非公開のウェブサーバー内部でトークンエンドポイントを呼び出す Webサービスのような利用形態なら、認可鯖が適切に検査していれば、 クライアントの credentials が流出しない限りにおいて、 たとえ認可符号が流出したとしても攻撃は防げます。 ところが一般配布されるスマートフォンアプリのような利用形態だと、 クライアントの credentials は攻撃者も入手可能な状態にあるので、 認可符号の流出は致命的な問題になります。

[44] 流出の可能性として考えられるのは、

などでしょうか。

[54] 認可鯖は、認可符号からアクセストークンの発行を求められている場合、 認可エンドポイントへのアクセス時に redirect_uri 引数が指定されていたなら本要求にも redirect_uri 引数が含まれており、両者の値が一致することを確認しなければなりません >>52, >>89。

[62] 認可鯖は、資源所有者の credentials からアクセストークンの発行を求められている場合、 指定された credentials を検証しなければなりません >>52。

[3] 認可鯖は、更新トークンからアクセストークンの発行を求められている場合、 指定された更新トークンを検証しなければなりません >>1。 クライアント認証可能であれば、正しいクライアントか検証しなければなりません >>89。

[63] 認可鯖は、回数制限や警告などにより、資源所有者合言葉credentials の総当たり攻撃からエンドポイントを保護しなければなりません >>52。

[95] 認可鯖は CSRF 対策が必要です。資源所有者の credentials を第三者に漏らしてしまう形の攻撃は難しそうですが、認可鯖 (や間接的にクライアント) に対する DoS攻撃や、 CSRF を引き起こされた被害者たる Webブラウザーの利用者への嫌がらせには使えるかもしれません。

[96] その性質上、CSRF 対策用のトークンを指定させるような方法は使えません。 Origin: ヘッダーの値とクライアントの登録情報を比較して不適切な要求を検出するのがよいでしょうか。

トークンエンドポイントの応答

[56] 認可鯖は、アクセストークンの発行を求められている場合、 要求が妥当であり認可されたなら、アクセストークンを発行します >>55, >>64, >>67, >>68, >>1。

[4] 認可符号や資源所有者合言葉credentials、拡張の承諾型では、 更新トークンも発行することもできます >>55, >>64, >>68。 ただしクライアント認証していない場合には発行するべきではないかもしれません >>89。

[5] クライアントcredentialsでは、 更新トークンを発行するべきではありません >>67。

[6] 更新トークンからアクセストークンを求めている場合には、 新しい更新トークンを発行しても構いません。その適用範囲 (scope) は元の更新トークンと同じでなければなりません。 その場合には認可鯖は古い更新トークンを取り消し (revoke) して構いませんし、クライアントは古い更新トークンを破棄しなければなりません。 >>1

[72] その場合には、 200 応答で次の引数を JSON (application/json) payload body の JSONオブジェクトの名前と値 (文字列なら JSON文字列、 数値なら JSON数値) に含めます >>71。

payload body (JSONオブジェクト)
[73] access_token
認可鯖が発行したアクセストークンを指定しなければなりません >>71。
[74] token_type
発行したトークンの種類を指定しなければなりません >>71。
[75] expires_in
アクセストークンの寿命を秒単位で指定するべきです >>71。
[76] refresh_token
同じ認可承諾を使って新しいアクセストークンを得られる更新トークンを指定できます >>71。
[77] scope
クライアントが要求した適用範囲とアクセストークンの適用範囲が異なるなら、指定しなければなりません。同じ場合も指定できます。 >>71
[107] id_token
OpenID Connect の場合には指定しなければなりません >>108。

[78] どれが文字列でどれが数値かは明確ではありませんが、 expires_in は数値、それ以外は文字列と思われます。

[106] アクセストークン型によっては、更に追加の引数を指定する必要があるかもしれません。

[117] grant_type=urn:ietf:params:oauth:grant-type:migration:oauth1 では、 refresh_token (更新トークン) のみが返されることになっていました。

[121] Azure は resource 引数と expires_on 引数を追加しています >>119。

[134] ヤマレコは token_type 引数を指定せず、 成功時でも error 引数と error_message 引数 (独自) を指定するようです >>133。

[148] Instagram は token_type 引数を指定せず、 user 引数 (独自) を指定するようです >>147。

user の値は JSONオブジェクトです。

[173] Bitly は apiKey 引数 (独自) を指定するようです。 ただし非推奨で削除予定とあります。 >>172

[179] Heroku は user_id、session_nonce を指定するようです >>178。

[182] Yahoo! は xoauth_yahoo_guid を指定するようです >>181。

[198] feedly は plan を指定するようです >>197。

[143] foursquare など >>161, >>163, >>142, >>172 は token_type 引数を指定しないようです。

[194] Facebook は token_type 引数を指定しません。 expires_in ではなく expires 引数に time_t 値を指定するようです。

[152] Salesforce は token_type 引数を指定しないようです。 独自の id, instance_url, issued_at, signature を指定するようです。 >>149, >>156, >>157

[151] Salesforce は要求で format 引数を指定した場合に応答が他のMIME型になるようです >>149, >>157。

[167] GitHub は要求で Accept: を指定した場合に応答が他の MIME型になるようです。既定値は application/x-www-form-urlencoded になっているようです。 >>166

[193] Facebook は応答に application/x-www-form-urlencoded を使うようです。ただし Content-Type: はなぜか text/plain; charset=UTF-8 になります。

[30] その後 JSON に修正 (非互換変更) されています >>29。


[42] 応答の引数は、複数指定してはなりません >>34。

[79] 更に、 Cache-Control: no-store と Pragma: no-cache を含めなければなりません >>71。

[80] クライアントは、認識できない名前の引数を無視しなければなりません >>71。


[56] 認可鯖は、アクセストークンの発行を求められている場合、 クライアント認証に失敗したか要求が非妥当であったなら、 誤り応答を返します >>55, >>64, >>67, >>68, >>1。

[88] その場合には、 400 応答で次の引数を JSON (application/json) payload body の JSONオブジェクトの名前と値 (文字列なら JSON文字列、 数値なら JSON数値) に含めます >>87。

payload body (JSONオブジェクト)
[84] error
誤り符号を指定しなければなりません >>87。
[85] error_description
人間可読な誤りの説明を指定して構いません >>87。
[86] error_uri
人間可読な誤りの説明を含むWebページの URL を指定して構いません >>87。

[126] 誤り符号によっては、他の HTTP 状態符号を使うこともあるようです。

[127] Azure は timestamp、trace_id、 correlation_id、error_codes を追加しています >>128。

[51] error が invalid_client の場合には、 401 応答とすることもできます。 クライアントが Authorization: ヘッダーで認証を試みた場合には、 401 応答と WWW-Authenticate: ヘッダー (auth-scheme は要求で指定されたもの) を使って応答しなければなりません >>87。

メモ

[196] draft-richer-oauth-xml-01 - Alternate Encoding for OAuth 2 Token Responses ( 版) https://tools.ietf.org/html/draft-richer-oauth-xml-01

[26] Twitter Login にも CSRF 脆弱性ができやすい罠が!? - OAuth.jp (Nov Matake著, ) http://oauth.jp/blog/2014/06/23/csrf-on-twitter-login/

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>

[27] Private-Webmention - IndieWeb () https://indieweb.org/Private-Webmention

Link: https://aaronpk.example/token; rel="token_endpoint"

[28] token-endpoint - IndieWeb () https://indieweb.org/token-endpoint

[29] Changelog(更新履歴) - Graph API ( ()) https://developers.facebook.com/docs/apps/changelog#v2_3_changes

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に準拠するために、このアップデートを行いました。

[31] Slack は OAuth 2.0 対応していることになっているが、トークンエンドポイント相当は似て非なる、 互換性のない別物。