コマンドラインでoauthヘッダ計算するやつ(3-legged)

# コマンドラインでoauthヘッダ計算するやつ(3-legged)

最初bashだけでなんとかできないかなー、と頑張ってみたけど、
`urlencode`のとこだけはどうにもならなくて、そこだけ妥協してperl使う事にした。

のはいいものの、
じゃあもう全部perlでいいじゃん、ってことで結局こうなった。

なんとかbashだけでやりたかったんだけどなぁ…

## 計算するやつ

* CPANで適当に必要なモジュール入れておいて。
* エラーチェックとかしてないので使うときは注意。
* 要書換箇所。
  * `CONSUMER_KEY`
  * `CONSUMER_SECRET`
  * `ACCESS_KEY`
  * `ACCESS_SECRET`
* 2-leggedは対応してないです。
  * けど、渡すパラメータ削るだけなのでちょっと手直しすればいけると思う。
* `get` 以外も対応してないです。
  * post dataがある場合は署名に含めないとならないので、めんどくさいから非対応。

```perl:oauth_header.pl
#!/usr/bin/perl

use strict; 
use warnings;
use URI::Escape;
use MIME::Base64;
use Digest::HMAC_SHA1;
use String::Random qw( random_regex );

use constant CONSUMER_KEY    => "YOUR_CONSUMER_KEY";
use constant CONSUMER_SECRET => "YOUR_CONSUMER_SECRET";
use constant ACCESS_KEY      => "YOUR_ACCESS_KEY";
use constant ACCESS_SECRET   => "YOUR_ACCESS_TOKEN";

sub oauth_header {
    my ($method, $url) = @_;

    my $timestamp = time();
    my $nonce     = sub {
        return random_regex('[a-zA-Z0-9_]{32}');
    }->();

    my $signature = sub {
        my $base_string = sub {
            my %parray = (
                'oauth_version'          => "1.0",
                'oauth_nonce'            => $nonce,
                'oauth_timestamp'        => $timestamp,
                'oauth_consumer_key'     => CONSUMER_KEY,
                'oauth_token'            => ACCESS_KEY,
                'oauth_signature_method' => "HMAC-SHA1",
            );

            my @sorted_params = ();
            foreach my $key (sort keys %parray) {
                push(@sorted_params, $key . "=" . $parray{$key});
            }
            return uri_escape( join("&", @sorted_params) );
        }->();

        my $sig_string = uc($method) . "&" . uri_escape($url) . "&" . $base_string;
        my $hmac_sha1_key = CONSUMER_SECRET . "&" . ACCESS_SECRET;
        my $sig_base64 = encode_base64(Digest::HMAC_SHA1::hmac_sha1($sig_string, $hmac_sha1_key));
        chomp($sig_base64);

        return uri_escape($sig_base64);
    }->();

    my %params = (
        "oauth_consumer_key"     => CONSUMER_KEY,
        "oauth_nonce"            => $nonce,
        "oauth_signature"        => $signature,
        "oauth_signature_method" => "HMAC-SHA1",
        "oauth_timestamp"        => $timestamp,
        "oauth_token"            => ACCESS_KEY,
        "oauth_version"          => "1.0",
    );

    my @joined_params = ();
    foreach my $key (sort keys %params) {
        push(@joined_params, $key . '="' . $params{$key} . '"');
    }

    my $header = "Authorization: OAuth ";
    return $header . join(", ", @joined_params);
}


my $method = $ARGV[0];
my $url    = $ARGV[1];

print oauth_header($method, $url);
```

## 使うやつ

とりあえずtwitterのtimeline叩いてみる。

post data無しなら`post`とかでもたぶんいける。

```bash:access.sh
#!/bin/bash

METHOD=GET
URL=https://api.twitter.com/1.1/statuses/home_timeline.json

curl --silent -X $METHOD --header "`./oauth_header.pl $METHOD $URL`" $URL
```

## ちなみにbashだと

途中までbashで書いてたついでにいろいろ調べたので、参考までに。

### hmac-sha1

`echo -n "hoge" | openssl dgst -sha1 -hmac "key"`

### base64

`echo -n "hoge" | openssl base64`

### nonce

`/dev/urandom` と base64あたり使えばなんとかなると思う。

### タイムスタンプ

`date +%s`

### urlencode

途中まで`nkf`でいけそうな気がしたけど、
これだとアンダースコアまでエンコードされてしまうので諦めた。

`echo ほげほげ | nkf -wMQ | tr = %`

もしかしたら `sed -e "s/\%5F/_/g"` に流せば戻せるのかもしれないけど、
そもそもちゃんとした`urlencode`用の処理じゃないっぽいので、
他にもおかしなところがありそうなので、断念。