メモ:OAuth 1.0のRFCをチラ見する

悔しいので前回のリベンジ的な。

notchained.hatenablog.com

Request token

他のはいいとして、Authorizationヘッダを見る。ドキュメントに乗ってるやつはこれ。

Authorization: OAuth realm="",
    oauth_callback="oob",
    oauth_consumer_key="yTVGWKqa6OiH5A%3D%3D",
    oauth_nonce="0c670efea71547422662",
    oauth_signature="lvQC7AXTRIaqxbjwVGgPlYuNaaw%3D",
    oauth_signature_method="HMAC-SHA1",
    oauth_timestamp="1291689730",oauth_version="1.0"

各パラメータの意味

RFC5849による各パラメータの説明。

oauth_callback

oauth_callback: An absolute URI back to which the server will redirect the resource owner when the Resource Owner Authorization step (Section 2.2) is completed. If the client is unable to receive callbacks or a callback URI has been established via other means, the parameter value MUST be set to “oob” (case sensitive), to indicate an out-of-band configuration.
(section 2.1)

After receiving an authorization decision from the resource owner, the server redirects the resource owner to the callback URI if one was provided in the “oauth_callback” parameter or by other means.
(section 2.2)

これはこの例ではoobになってるけど、httrでやるときはhttp://localhost:1410/になる。

oauth_consumer_key

oauth_consumer_key: The identifier portion of the client credentials (equivalent to a username). …(略)
(section 3.1)

これはこのまま指定して問題ないはず。

oauth_nonce

oauth_nonce
The nonce value as defined in Section 3.3. The parameter MAY be omitted when using the “PLAINTEXT” signature method. (section 3.1)

A nonce is a random string, uniquely generated by the client to allow the server to verify that a request has never been made before and helps prevent replay attacks when requests are made over a non-secure channel. The nonce value MUST be unique across all requests with the same timestamp, client credentials, and token combinations.

ということで、ランダムな値でよさそう。httrの実装を見ると、

nonce <- function(length = 10) {
  paste(sample(c(letters, LETTERS, 0:9), length, replace = TRUE),
    collapse = "")
}

になっている。sample()でやるのはいいのかな…。まあいったん気にしないことにする。

oauth_signature, oauth_signature_method

これはちょっとややこしいのでいったんスキップ。

oauth_timestamp
   `oauth_timestamp`  
     The timestamp value as defined in Section 3.3.  The parameter
     MAY be omitted when using the "PLAINTEXT" signature method.  

(section 3.1)

The timestamp value MUST be a positive integer. Unless otherwise specified by the server’s documentation, the timestamp is expressed in the number of seconds since January 1, 1970 00:00:00 GMT.
(section 3.3)

oauth_version

oauth_version
OPTIONAL. If present, MUST be set to “1.0”.
(section 3.1)

これはあってもなくてもいいらしい。

シグネチャの組み立て方

さっきスキップしたやつ。section 3.4に書かれているので読む。

まずメソッドを大文字で書く。

  1. . The HTTP request method in uppercase. For example: “HEAD”, “GET”, “POST”, etc. If the request uses a custom HTTP method, it MUST be encoded (Section 3.6).
POST

次に&を付ける

POST&

エンコードしたbase URIを付ける。エンコード方式はsection 3.6にある。RFC3986のパーセントエンコーディングと一致する保証はないらしいけど、まあ大まかに言って、(1) UTF-8に変換、(2)パーセントエンコーディング、と考えてよさそう。

ということでこれを付け加える。

URLencode("https://www.hatena.com/oauth/initiate", reserved = TRUE)
#> [1] "https%3A%2F%2Fwww.hatena.com%2Foauth%2Finitiate"
POST&https%3A%2F%2Fwww.hatena.com%2Foauth%2Finitiate

さらに&を付ける

POST&https%3A%2F%2Fwww.hatena.com%2Foauth%2Finitiate&

次、残りのパラメータをnormalizeしてエンコードする。「残りのパラメータ」は以下。

  • The query component of the HTTP request URI as defined by [RFC3986], Section 3.4. The query component is parsed into a list of name/value pairs by treating it as an “application/x-www-form-urlencoded” string, separating the names and values and decoding them as defined by [W3C.REC-html40-19980424], Section 17.13.4.
  • The OAuth HTTP “Authorization” header field (Section 3.5.1) if present. The header’s content is parsed into a list of name/value pairs excluding the “realm” parameter if present. The parameter values are decoded as defined by Section 3.5.1.
  • The HTTP request entity-body, but only if all of the following conditions are met:
    • The entity-body is single-part.
    • The entity-body follows the encoding requirements of the “application/x-www-form-urlencoded” content-type as defined by [W3C.REC-html40-19980424].
    • The HTTP request entity-header includes the “Content-Type” header field set to “application/x-www-form-urlencoded”.

    The entity-body is parsed into a list of decoded name/value pairs as described in [W3C.REC-html40-19980424], Section 17.13.4.

つまり今回はこう。

rest_params <- list(
  oauth_callback="oob",
  oauth_consumer_key="yTVGWKqa6OiH5A==",
  oauth_nonce="0c670efea71547422662",
  oauth_signature_method="HMAC-SHA1",
  oauth_timestamp="1291689730",
  oauth_version="1.0",
  # 上には抜き出してなかったけどbodyに含まれるパラメータ。いったんデコードする。
  scope="read_public,read_private"
)

このパラメータの名前と値をエンコードする。

names(rest_params) <- curl::curl_escape(names(rest_params))
rest_params <- lapply(rest_params, curl::curl_escape)

これをパラメータ名のアルファベット順にソートして、それぞれの名前と値を=でつなぐ。

# 並べ替え
rest_params <- rest_params[sort(names(rest_params))]
# =でつなげる
rest_params <- paste(names(rest_params), rest_params, sep = "=")

これを&でつなぐ。

rest_params <- paste(rest_params, collapse = "&")

で、これをさらにエンコードする。

curl::curl_escape(rest_params)
#> [1] "oauth_callback%3Doob%26oauth_consumer_key%3DyTVGWKqa6OiH5A%253D%253D%26oauth_nonce%3D0c670efea71547422662%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1291689730%26oauth_version%3D1.0%26scope%3Dread_public%252Cread_private"

合わせるとこう。これがbase stringらしい。

POST&https%3A%2F%2Fwww.hatena.com%2Foauth%2Finitiate&oauth_callback%3Doob%26oauth_consumer_key%3DyTVGWKqa6OiH5A%253D%253D%26oauth_nonce%3D0c670efea71547422662%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1291689730%26oauth_version%3D1.0%26scope%3Dread_public%252Cread_private

次にこの文字列からHMAC-SHA1シグネチャをつくる。

署名に使う鍵は以下のように定められている。

key is set to the concatenated values of: 1. The client shared-secret, after being encoded (Section 3.6). 2. An “&” character (ASCII code 38), which MUST be included even when either secret is empty. 3. The token shared-secret, after being encoded (Section 3.6).

とここまで書いて気付いたけど、sercretで署名するから、ドキュメントにはsecretが載ってない以上シグネチャの再現はできないのでした。

ということでこれはここで断念…