Search code examples
perlapitwitteroauthsign

Twitter OAuth 1.0 authentication with signature in perl


I'm getting this problem with my app in perl for oauth authentication:

401 Unauthorized Failed to validate oauth signature and token

Here is my code:

sub Twitter {

my $IN = new CGI;

  my $qs = build_query({
    oauth_callback => $callback_url,
    oauth_consumer_key => $consumer_key,
    oauth_nonce => time,
    oauth_signature_method => "HMAC-SHA1",
    oauth_timestamp => time,
    oauth_version => "1.0"
  });

# Create Signature

  my $signing_key = $IN->escape($consumer_secret)."&";

  my $base_signature = "POST&".$IN->escape($request_token_url)."&".$qs;

  use Digest::HMAC_SHA1;

  my $hmac = Digest::HMAC_SHA1->new($signing_key);
  $hmac->add($base_signature);

  $qs .= "&oauth_signature=".$IN->escape($hmac->b64digest);

# Fetch the page

  use LWP;
  my $ua = LWP::UserAgent->new;
  my $req = HTTP::Request->new(POST => $request_token_url);
  $req->content_type('application/x-www-form-urlencoded');
  $req->content($qs);

  my $res = $ua->request($req);

# Check the outcome of the response
  unless ($res->is_success) {
    print $IN->header.$res->status_line, "\n";
    print $res->content;
    exit;
  }
  print $IN->header.$res->content;
}
sub build_query {
  my $input = shift;
  use URI;
  my $uri = URI->new;
  $uri->query_form($input);
  return $uri->query;
}

I have obviously deleted my callback url and key information.


Solution

  • I figured it out. I was encoding the signature wrong, I had to sort my query strings, and the call back URL is not needed in this instance. Here is my working code:

    sub Twitter {
    
      my $IN = new CGI;
    
      my $params = {
        oauth_consumer_key => $consumer_key,
        oauth_nonce => time,
        oauth_signature_method => "HMAC-SHA1",
        oauth_timestamp => time,
        oauth_version => "1.0"
      };
      my $qs = build_sorted_query($params);
    
      my $signing_key = $IN->escape($consumer_secret)."&";
    
      my $signature_base = "POST&".$IN->escape($request_token_url)."&".$IN->escape($qs);
    
      use Digest::HMAC_SHA1;
      use MIME::Base64;
    
      my $hmac = Digest::HMAC_SHA1->new($signing_key);
      $hmac->add($signature_base);
    
      $params->{oauth_signature} = $IN->escape(encode_base64($hmac->digest));
    
      $qs = build_sorted_query($params);
    
      use LWP;
      my $ua = LWP::UserAgent->new;
      my $req = HTTP::Request->new(POST => $request_token_url);
      $req->content_type('application/x-www-form-urlencoded');
      $req->content($qs);
    
      my $res = $ua->request($req);
    
    # Check the outcome of the response
      unless ($res->is_success) {
        print $IN->header.$res->status_line, "\n";
        print $res->content;
        exit;
      }
      print $IN->header.$res->content;
      return;
    }
    sub build_sorted_query {
      my $input = shift;
      my $qs;
      foreach (sort keys %$input) {
          $qs .= $_."=".$input->{$_}."&";
      }
      return substr ($qs, 0, -1);
    }
    

    Thanks for looking!