summary refs log tree commit diff
diff options
context:
space:
mode:
authorfranck cuny <franck@lumberjaph.net>2011-07-22 13:51:15 +0200
committerfranck cuny <franck@lumberjaph.net>2011-07-26 13:21:02 +0200
commit597df56b91a2f68a7304f8662af354f95f9eda33 (patch)
treedc6303830ab6def532c421bf3f44fc5c1408884f
parentfix bug with trace set to 0 (diff)
downloadnet-http-spore-597df56b91a2f68a7304f8662af354f95f9eda33.tar.gz
working OAuth middleware
Signed-off-by: franck cuny <franck@lumberjaph.net>
-rw-r--r--lib/Net/HTTP/Spore/Middleware/Auth/OAuth.pm235
1 files changed, 203 insertions, 32 deletions
diff --git a/lib/Net/HTTP/Spore/Middleware/Auth/OAuth.pm b/lib/Net/HTTP/Spore/Middleware/Auth/OAuth.pm
index 524205d..ffff80b 100644
--- a/lib/Net/HTTP/Spore/Middleware/Auth/OAuth.pm
+++ b/lib/Net/HTTP/Spore/Middleware/Auth/OAuth.pm
@@ -3,61 +3,232 @@ package Net::HTTP::Spore::Middleware::Auth::OAuth;
 # ABSTRACT: middleware for OAuth authentication
 
 use Moose;
-extends 'Net::HTTP::Spore::Middleware::Auth';
-
-use Net::OAuth;
+use URI::Escape;
+use Digest::SHA;
 use MIME::Base64;
 
-has [qw/consumer_key consumer_secret token token_secret/] => (
+extends 'Net::HTTP::Spore::Middleware::Auth';
+
+has [qw/oauth_consumer_key oauth_consumer_secret/] => (
     is       => 'ro',
     isa      => 'Str',
     required => 1,
 );
 
+has oauth_callback => (
+    is      => 'ro',
+    isa     => 'Str',
+    lazy    => 1,
+    default => 'oob',
+);
+
+has oauth_signature_method => (
+    is      => 'ro',
+    isa     => 'Str',
+    lazy    => 1,
+    default => 'HMAC-SHA1',
+);
+
+has [qw/oauth_token oauth_token_secret oauth_verifier realm/] => (
+    is  => 'ro',
+    isa => 'Str',
+);
+
 sub call {
     my ( $self, $req ) = @_;
 
     return unless $self->should_authenticate($req);
 
-    my $uri = $req->uri;
-    my $request = Net::OAuth->request('protected resource')->new(
-        version          => '1.0',
-        consumer_key     => $self->consumer_key,
-        consumer_secret  => $self->consumer_secret,
-        token            => $self->token,
-        token_secret     => $self->token_secret,
-        request_method   => $req->method,
-        signature_method => 'HMAC-SHA1',
-        timestamp        => time,
-        nonce            => MIME::Base64::encode( time . $$ . rand, '' ),
-        request_url      => $req->uri,
-        # extra_params     => \%post_args,
+    my $oauth_params = {
+        oauth_signature_method => $self->oauth_signature_method,
+        oauth_consumer_key     => $self->oauth_consumer_key,
+        oauth_token            => $self->oauth_token,
+        oauth_verifier         => $self->oauth_verifier,
+        oauth_version          => '1.0',
+    };
+
+    if ( !defined $oauth_params->{oauth_token} ) {
+        $oauth_params->{oauth_callback} = $self->oauth_callback;
+    }
+
+    foreach my $k ( keys %$oauth_params ) {
+        $oauth_params->{$k} = uri_escape( $oauth_params->{$k} );
+    }
+
+    $req->finalize;
+
+    my $oauth_sig = $self->_oauth_sig( $req, $oauth_params );
+    $req->header( 'Authorization' =>
+          $self->_build_auth_string( $oauth_params, $oauth_sig ) );
+}
+
+sub _base_string {
+    my ($self, $req, $oparams) = @_;
+
+    my $query_keys = [];
+    my $query_vals = {};
+
+    if ( defined $req->env->{QUERY_STRING} ) {
+        while ($req->env->{QUERY_STRING} =~ /([^=]+)=([^&]*)&?/g){
+            my ($k,$v) = ($1,$2);
+            push @$query_keys, $k;
+            $query_vals->{$k} = $v;
+        }
+    }
+
+    my $payload = $req->body;
+    if ( defined $payload ) {
+        my $ct = $req->header('content-type');
+        if ( !defined $ct or $ct eq 'application/x-www-form-urlencoded' ) {
+            while ($payload =~ /([^=]+)=([^&]*)&?/g){
+                my ($k,$v) = ($1,$2);
+                $v =~ s/\+/\%\%20/;
+                push @$query_keys, $k;
+                $query_vals->{$k} = $v;
+            }
+        }
+    }
+
+    my $scheme = $req->scheme;
+    my $port   = $req->port;
+
+    if ($port == 80 and $scheme eq 'http'){
+        $port = undef;
+    }
+    if (defined $scheme && $port == 443 and $scheme eq 'https'){
+        $port = undef;
+    }
+
+    my $uri =
+        ( $scheme || 'https' ) . "://"
+      . $req->env->{SERVER_NAME}
+      . $req->env->{SCRIPT_NAME}
+      . $req->env->{PATH_INFO};
+
+    foreach my $k (keys %$oparams){
+        push @$query_keys, $k;
+        $query_vals->{$k} = $oparams->{$k};
+    }
+
+    my @sort = sort {$a cmp $b} @$query_keys;
+    my $params = [];
+
+    foreach my $k (@sort){
+        my $v = $query_vals->{$k};
+        push @$params, $k . '=' . $v if defined $v;
+    }
+    my $normalized = join('&', @$params);
+    my $str = uc($req->method) . '&' . uri_escape($uri) . '&' . uri_escape($normalized);
+    return $str;
+}
+
+sub _build_auth_string {
+    my ( $self, $oauth_params, $oauth_sig ) = @_;
+
+    my $auth = 'OAuth';
+
+    if ( $self->realm ) {
+        $auth = $auth . ' realm="' . $self->realm . '",';
+    }
+
+    $auth =
+        $auth
+      . ' oauth_consumer_key="'
+      . $oauth_params->{oauth_consumer_key} . '"'
+      . ', oauth_signature_method="'
+      . $oauth_params->{oauth_signature_method} . '"'
+      . ', oauth_signature="'
+      . $oauth_sig . '"';
+
+    if ( $oauth_params->{oauth_signature_method} ne 'PLAINTEXT' ) {
+        $auth =
+            $auth
+          . ', oauth_timestamp="'
+          . $oauth_params->{oauth_timestamp} . '"'
+          . ', oauth_nonce="'
+          . $oauth_params->{oauth_nonce} . '"';
+    }
+
+    if ( !$oauth_params->{oauth_token} ) {
+        $auth =
+          $auth . ', oauth_callback="' . $oauth_params->{oauth_callback} . '"';
+    }
+    else {
+        if ( $oauth_params->{oauth_verifier} ) {
+            $auth =
+                $auth
+              . ', oauth_token="'
+              . $oauth_params->{oauth_token} . '"'
+              . ', oauth_verifier="'
+              . $oauth_params->{oauth_verifier} . '"';
+        }
+        else {
+            $auth =
+              $auth . ', oauth_token="' . $oauth_params->{oauth_token} . '"';
+        }
+    }
+
+    $auth = $auth . ', oauth_version="' . $oauth_params->{oauth_version} . '"';
+    return $auth;
+}
+
+sub _oauth_sig {
+    my ( $self, $req, $oauth_params ) = @_;
+
+    die $oauth_params->{oauth_signature_method} . " is not supported"
+      unless ( $oauth_params->{oauth_signature_method} eq 'PLAINTEXT'
+        || $oauth_params->{oauth_signature_method} eq 'HMAC-SHA1' );
+
+    if ( $oauth_params->{oauth_signature_method} eq 'PLAINTEXT' ) {
+        return uri_escape( $self->_signature_key );
+    }
+
+    $oauth_params->{oauth_timestamp} = time;
+    $oauth_params->{oauth_nonce}     = $self->_oauth_nonce;
+
+    my $oauth_signature_base_string = $self->_base_string( $req, $oauth_params );
+
+    return uri_escape(
+        MIME::Base64::encode_base64(
+            Digest::SHA::hmac_sha1(
+                $oauth_signature_base_string, $self->_signature_key
+            )
+        )
     );
+}
 
-    $request->sign;
-    my $auth = $request->to_authorization_header;
-    $req->header( 'Authorization' => $auth );
+sub _oauth_nonce {
+    Digest::SHA::sha1_hex( rand() . 'random' . time() . 'keyyy' );
+}
+
+sub _signature_key {
+    my $self = shift;
+    my $signature_key =
+        uri_escape( $self->oauth_consumer_secret ) . '&'
+      . uri_escape( $self->oauth_token_secret || '' );
+    return $signature_key;
 }
 
 1;
 
 =head1 SYNOPSIS
 
-    my $client = Net::HTTP::Spore->new_from_spec('twitter.json');
-
+    my $client = Net::HTTP::Spore->new_from_spec( 'google-url-shortener.json' );
     $client->enable('Format::JSON');
-
-    $client->enable(
-        'Auth::OAuth',
-        consumer_key    => 'xxx',
-        consumer_secret => 'yyy',
-        token           => '123',
-        token_secret    => '456'
+    $client->enable('Auth::OAuth',
+        oauth_consumer_key    => '00000000.apps.googleusercontent.com',
+        oauth_consumer_secret => 'xxxxxxxxx',
+        oauth_token           => 'yyyyyyyyy',
+        oauth_token_secret    => 'zzzzzzzzz',
     );
 
-    print $client->friends_timeline(
-      format => 'json'
-    )->body->[0]->{text};
+    my $r = $client->insert( payload => { longUrl => 'http://f.lumberjaph.net/' } );
+    say( $r->body->{id} . ' is ' . $r->body->{longUrl} );
+    say "list >";
+    $r = $client->list();
+    foreach my $short (@{$r->body->{items}}){
+       say $short->{id} . '  ' . $short->{longUrl};
+    }
 
 =head1 DESCRIPTION