名もなき未知

エンジニアリングとか、日常とかそういうのをまとめる場所。アクセス解析のためGAを利用、Googleに情報を送信しています。商品紹介のためAmazonアフィリエイトを利用、Amazonに情報を送信しています。記事に関しては私が書いていない引用文を除いて自由にご利用ください。

はてなAPIを叩くためにoauth_signatureを作成する(Python3で)

urllib.request で十分に通信ができてしまうので、requestsわざわざ使う必要もないかなーと思って(実際Lambdaにあげようとしているのでライブラリは少ないほうが助かる)、oauth_signatureを自作していたのですが、まあ結構辛かったので、メモです。

コード辺

本当に大変だった。概念的には下記みたいな感じ。リクエスト部分はまた今度。

ACCESS_POINT = (
    "https://blog.hatena.ne.jp/MireiMixin/namonakimichi.hatenablog.com/atom/entry"
)

class MethodType(Flag):
    GET = auto()
    POST = auto()
    PUT = auto()
    DELETE = auto()
    WITH_BODY = POST | PUT | DELETE


METHOD_TYPE_STRING = {
    MethodType.GET: "GET",
    MethodType.POST: "POST",
    MethodType.PUT: "PUT",
    MethodType.DELETE: "DELETE",
}

RequestParams = NewType("RequestParams", Union[str, int])
RequestDict = NewType("RequestDict", Dict[str, RequestParams])

def load_access_keys() -> Dict[str, str]:
    keys = ("consumer_key", "consumer_secret_key", "token_key", "token_secret_key")
    return {key: os.getenv(key.upper()) for key in keys}


def create_auth_signature(url, method, baseparam, consumer_keys):
    signature = dict(baseparam)
    signature = "&".join(
        "{0}={1}".format(quote(key, "~"), quote(signature[key], "~"))
        for key in sorted(signature)
    )
    signature = (
        "{0}&{1}".format(
            quote(consumer_keys["consumer_secret_key"], "~"),
            quote(consumer_keys["token_secret_key"], "~"),
        ),
        "{0}&{1}&{2}".format(
            quote(METHOD_TYPE_STRING[method].upper()),
            quote(url, "~"),
            quote(signature, "~"),
        ),
    )
    key_utf, text_utf = [s.encode("utf-8") for s in signature]
    signature = hmac.new(key_utf, text_utf, hashlib.sha1)
    signature = binascii.b2a_base64(signature.digest())[:-1].decode("utf-8")
    return signature


def create_oauth_header(url, method):
    consumer_keys = load_access_keys()
    baseparam = {
        "oauth_token": consumer_keys["token_key"],
        "oauth_consumer_key": consumer_keys["consumer_key"],
        "oauth_signature_method": "HMAC-SHA1",
        "oauth_timestamp": str(int(time.time())),
        "oauth_nonce": str(random.getrandbits(64)),
        "oauth_version": "1.0",
    }
    oauth_signature = create_auth_signature(url, method, baseparam, consumer_keys)
    header = dict(baseparam)
    header.update({"oauth_signature": oauth_signature})
    header = ",".join(
        "{0}={1}".format(quote(k, "~"), quote(header[k], "~")) for k in sorted(header)
    )
    return {
        "Authorization": "OAuth {0}".format(header),
        "Content-Type": "application/xml; charset=utf-8",
    }


def auth_request(url, method, body):
    header = create_oauth_header(url, method)
    return request(
        url=url,
        headers=RequestDict(header),
        with_encode=True,
        body=RequestDict(body),
    )

参考にしたページ

まとめ

リクエスト部分はまた今度で。。。はてなブログAPIを叩くためのAWS LambdaをServerless Frameworkで建てる、みたいな記事を書く予定。

二回もやりたくないかなこれは・・・ 最終的にはRequestsの中身をかなり読んで、それを参考に実装しました。Python3の実装がその辺に転がっていなかったのも結構辛かったです。

とりあえずもう少し上は整理される予定ですが、一旦記事にしとかないとどういうロジックだったか忘れるのでブログにメモだけ残しておきます。