2

I'm having trouble using basic authentication in WWW::Mechanize. I'm trying to connect to the Streak API, the documentation for which states:

Streak uses HTTP Basic Auth to sign each request with your API key. Simply set the username of the request to the API key. The password field is ignored. All requests must be made over HTTPS as HTTP requests will be ignored.

Here's a sample request:

curl https://www.streak.com/api/v1/pipelines -u YOUR_API_KEY:

I can successfully access the API using curl in this fashion. However, I'm not able to authenticate successfully using WWW::Mechanize. Here's what I've got:

#!perl

use warnings;
use strict;
use feature 'say';

use WWW::Mechanize;

my $api = 'https://www.streak.com/api/v1/';

my $mech = WWW::Mechanize->new( autocheck => 0 ); # don't die on errors

$mech->credentials('my API key here', '');

$mech->get($api . 'pipelines');

say $mech->response->status_line;
say $mech->res->request->as_string;

Running me that code gets:

401 Unauthorized
GET https://www.streak.com/api/v1/pipelines
Accept-Encoding: gzip
User-Agent: WWW-Mechanize/1.83

The authentication isn't even being attempted. Can anyone suggest why that may be the case, and what I could do to fix it? This code is running in Strawberry Perl 5.24.0.1, if that may have anything to do with it.

[Edited to include suggestion from simbabque of examining request object.]

Scott Martin
  • 1,260
  • 2
  • 17
  • 27
  • 1
    Have you compared the HTTP::Request object that WWW::Mechanize creates with the curl request? You can use `$mech->res->request->as_string` to get the Perl side of things, and `curl -v` for curl. – simbabque Nov 03 '16 at 15:09
  • Interesting. That reveals that it's not doing the authentication at all: `GET https://www.streak.com/api/v1/pipelines` `Accept-Encoding: gzip` `User-Agent: WWW-Mechanize/1.83` – Scott Martin Nov 03 '16 at 15:34
  • I saw the same thing. Very strange. If you supply a longer string it also doesn't do it. But I have code that uses this feature. We're overlooking something I believe. – simbabque Nov 03 '16 at 16:13
  • I've just tried the same thing using LWP::UserAgent instead of WWW::Mechanize with exactly the same result, so clearly that's where the problem lies. Will update as I learn more. – Scott Martin Nov 04 '16 at 10:27

1 Answers1

4

I found the problem.

Following the technique in this post on Perl Maven ("How to find out the URL and realm?"), it turns out that the API doesn't send a challenge for credentials, specifying a realm, when you try connecting to it. It just gives you an error message stating that basic authentication is required. LWP::UserAgent doesn't know to do anything else at that point.

So, I copied the authentication header from the successful curl request that simbabque suggested examining, and manually set that on the user-agent object:

$ua->default_header('Authorization' => 'Basic [Base64-encoded string here]');

Now it works. Happy times.

Scott Martin
  • 1,260
  • 2
  • 17
  • 27
  • kudos for figuring this out and posting it here. – xxfelixxx Nov 05 '16 at 05:13
  • Hey, thanks @xxfelixxx. Hopefully this will be of use to anyone else using WWW::Mechanize and encountering a resource that doesn't do authentication properly. As it turns out, this API is violating [RFC 7235 (Hypertext Transfer Protocol (HTTP/1.1): Authentication) section 3.1.](https://tools.ietf.org/html/rfc7235#section-3.1) by not accompanying the 401 Unauthorized with a WWW-Authenticate header. – Scott Martin Nov 11 '16 at 14:14