Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask

Sending HTTP credentials with SOAP

by astroboy (Chaplain)
on Mar 17, 2010 at 01:37 UTC ( #829059=perlquestion: print w/replies, xml ) Need Help??
astroboy has asked for the wisdom of the Perl Monks concerning the following question:


In have a class generated by SOAP::WSDL for a resource that requires Basic Authentication. The usual way to provide the credentials is to put something like this in your code

*SOAP::WSDL::Transport::HTTP::get_basic_credentials = sub { return ($user, $password); };

The underlying HTTP transport, LWP::UserAgent, won't supply the credentials until it receives either a HTTP::Status::RC_UNAUTHORIZED or a HTTP::Status::RC_PROXY_AUTHENTICATION_REQUIRED status code (401 or 407, I believe). This is correct, as specified in RFC2616.

Unfortunately, the SOAP service returns a 501 status, and a fault stating that the Basic Authentication credentials were never received. So I need a away around this. One way to to edit the class generated by SOAP::WSDL, and supply the credentials in the proxy = e.g. http://user:password@service. However, I really want to avoid this, as it will mean that I've hard-coded the authentication details in a package, which then isn't shareable by anyone else wishing to call the same service, and if I need to generate packages from the WSDL again, I''ll lose the credentials

So can anyone see a way around this?

Replies are listed 'Best First'.
Re: Sending HTTP credentials with SOAP
by almut (Canon) on Mar 17, 2010 at 02:10 UTC
    ...edit the class generated by SOAP::WSDL, and supply the credentials in the proxy = e.g. http://user:password@service.

    Maybe call the same get_basic_credentials() routine from there that the transport layer would call (if it did get 401), instead of hardcoding the credentials. The idea being that the credentials would then be stored in the same place they'd be stored otherwise if the 401 or 407 mechanism did work...  (in case I've understood the problem correctly, that is :)

      I'm not quite sure how I'd get the opportunity to do that. Plus, if I understand you correctly, I'd be calling my own routine and returning the credentials to myself, not LWP::UserAgent.
Re: Sending HTTP credentials with SOAP
by Anonymous Monk on Mar 17, 2010 at 07:19 UTC
    Does help?
    { # a block just to scope "no warnings" no warnings qw(redefine); *LWP::UserAgent::get_basic_credentials = sub { my ($user, $password); # remove user from option if called, to force prompting for a +user # name the next time print "URL requires authorization.\n"; if (not $user = delete $opt{user}) { print 'User name:'; ReadMode 1; $user = ReadLine(); ReadMode 0; chomp $user; }; if (not $password = delete $opt{password}) { print 'Password:'; ReadMode 2; $password = ReadLine; ReadMode 0; chomp $password; }; return ($user, $password); }; }

      While monkeypatching LWP works, I prefer to derive a class from LWP::UserAgent that implements get_basic_credentials so it's possible to have connections to more than one server and to avoid interference with other modules using LWP::UserAgent.

        That is generally the way to do things, but author of is author of SOAP::WSDL, and my guess is he might know something we don't.
        Unfortunately, I don't think that helps me. The SOAP library would still call LWP, not my derived class. is what I used to generate the classes. I think the code you quoted is to read a WSDL file that is protected. Anyway, it would still have the same problem, since the code I quoted in the OP does pretty much the same thing ... providing a method to get called when challenged for authentication details (since LWP is used under the covers anyway). If LWP doesn't get a 401 HTTP response code, it won't send the authentication details
Re: Sending HTTP credentials with SOAP
by astroboy (Chaplain) on Mar 17, 2010 at 04:03 UTC

    OK, I've found a hokey solution (where My::Interface is the interface class generated by SOAP::WSDL):

    my $service = My::Interface->new; my $endpoint = $service->get_endpoint; $endpoint =~ s!://!://$user:$pass@!; $service->proxy($endpoint);

    This inserts "$user:$pass@" after the "http(s)://". It's not nice, but it works in this instance, but it could be a bit brittle ...

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://829059]
Approved by crashtest
Front-paged by crashtest
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (8)
As of 2018-06-25 10:30 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (126 votes). Check out past polls.