WWW-OAuth-1.002000755001750001750 014741612716 13230 5ustar00grinnzgrinnz000000000000README100644001750001750 2043014741612716 14210 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002NAME WWW::OAuth - Portable OAuth 1.0 authentication SYNOPSIS use WWW::OAuth; my $oauth = WWW::OAuth->new( client_id => $client_id, client_secret => $client_secret, token => $token, token_secret => $token_secret, ); # Just retrieve authorization header my $auth_header = $oauth->authorization_header($http_request, { oauth_callback => $url }); $http_request->header(Authorization => $auth_header); # HTTP::Tiny use HTTP::Tiny; my $res = $oauth->authenticate(Basic => { method => 'GET', url => $url }) ->request_with(HTTP::Tiny->new); # HTTP::Request use HTTP::Request::Common; use LWP::UserAgent; my $res = $oauth->authenticate(GET $url)->request_with(LWP::UserAgent->new); # Mojo::Message::Request use Mojo::UserAgent; my $tx = $ua->build_tx(get => $url); $tx = $oauth->authenticate($tx->req)->request_with(Mojo::UserAgent->new); DESCRIPTION WWW::OAuth implements OAuth 1.0 request authentication according to RFC 5849 (sometimes referred to as OAuth 1.0A). It does not implement the user agent requests needed for the complete OAuth 1.0 authorization flow; it only prepares and signs requests, leaving the rest up to your application. It can authenticate requests for LWP::UserAgent, Mojo::UserAgent, HTTP::Tiny, and can be extended to operate on other types of requests. Some user agents can be configured to automatically authenticate each request with a WWW::OAuth object. # LWP::UserAgent my $ua = LWP::UserAgent->new; $ua->add_handler(request_prepare => sub { $oauth->authenticate($_[0]) }); # Mojo::UserAgent my $ua = Mojo::UserAgent->new; $ua->on(start => sub { $oauth->authenticate($_[1]->req) }); RETRIEVING ACCESS TOKENS The process of retrieving access tokens and token secrets for authorization on behalf of a user may differ among various APIs, but it follows this general format (error checking is left as an exercise to the reader): use WWW::OAuth; use WWW::OAuth::Util 'form_urldecode'; use HTTP::Tiny; my $ua = HTTP::Tiny->new; my $oauth = WWW::OAuth->new( client_id => $client_id, client_secret => $client_secret, ); # Request token request my $res = $oauth->authenticate({ method => 'POST', url => $request_token_url }, { oauth_callback => $callback_url })->request_with($ua); my %res_data = @{form_urldecode $res->{content}}; my ($request_token, $request_secret) = @res_data{'oauth_token','oauth_token_secret'}; Now, the returned request token must be used to construct a URL for the user to go to and authorize your application. The exact method differs by API. The user will usually be redirected to the $callback_url passed earlier after authorizing, with a verifier token that can be used to retrieve the access token and secret. # Access token request $oauth->token($request_token); $oauth->token_secret($request_secret); my $res = $oauth->authenticate({ method => 'POST', url => $access_token_url }, { oauth_verifier => $verifier_token })->request_with($ua); my %res_data = @{form_urldecode $res->{content}}; my ($access_token, $access_secret) = @res_data{'oauth_token','oauth_token_secret'}; Finally, the access token and secret can now be stored and used to authorize your application on behalf of this user. $oauth->token($access_token); $oauth->token_secret($access_secret); ATTRIBUTES WWW::OAuth implements the following attributes. client_id my $client_id = $oauth->client_id; $oauth = $oauth->client_id($client_id); Client ID used to identify application (sometimes called an API key or consumer key). Required for all requests. client_secret my $client_secret = $oauth->client_secret; $oauth = $oauth->client_secret($client_secret); Client secret used to authenticate application (sometimes called an API secret or consumer secret). Required for all requests. token my $token = $oauth->token; $oauth = $oauth->token($token); Request or access token used to identify resource owner. Leave undefined for temporary credentials requests (request token requests). token_secret my $token_secret = $oauth->token_secret; $oauth = $oauth->token_secret($token_secret); Request or access token secret used to authenticate on behalf of resource owner. Leave undefined for temporary credentials requests (request token requests). signature_method my $method = $oauth->signature_method; $oauth = $oauth->signature_method($method); Signature method, can be PLAINTEXT, HMAC-SHA1, RSA-SHA1, or a custom signature method. For RSA-SHA1 or custom signature methods, a "signer" must be provided. Defaults to HMAC-SHA1. signer my $signer = $oauth->signer; $oauth = $oauth->signer(sub { my ($base_str, $client_secret, $token_secret) = @_; ... return $signature; }); Coderef which implements the "signature_method". A default signer is provided for signature methods PLAINTEXT and HMAC-SHA1; this attribute is required for other signature methods. For signature method RSA-SHA1, this attribute may also be an object which has a sign method like Crypt::OpenSSL::RSA. The signer is passed the computed signature base string, the client secret, and (if present) the token secret, and must return the signature string. METHODS WWW::OAuth implements the following methods. authenticate $container = $oauth->authenticate($container, \%oauth_params); my $container = $oauth->authenticate($http_request, \%oauth_params); my $container = $oauth->authenticate(Basic => { method => 'GET', url => $url }, \%oauth_params); Wraps the HTTP request in a container with "oauth_request" in WWW::OAuth::Util, then sets the Authorization header using "authorization_header" to sign the request for OAuth 1.0. An optional hashref of OAuth parameters will be passed through to "authorization_header". Returns the container object. authorization_header my $auth_header = $oauth->authorization_header($container, \%oauth_params); my $auth_header = $oauth->authorization_header($http_request, \%oauth_params); my $auth_header = $oauth->authorization_header(Basic => { method => 'GET', url => $url }, \%oauth_params); Forms an OAuth 1.0 signed Authorization header for the passed request. As in "authenticate", the request may be specified in any form accepted by "oauth_request" in WWW::OAuth::Util. OAuth protocol parameters (starting with oauth_ or the special parameter realm) may be optionally specified in a hashref and will override any generated protocol parameters of the same name (they should not be present in the request URL or body parameters). Returns the signed header value. HTTP REQUEST CONTAINERS Request containers provide a unified interface for "authenticate" to parse and update HTTP requests. They must perform the Role::Tiny role WWW::OAuth::Request. Custom container classes can be instantiated directly or via "oauth_request" in WWW::OAuth::Util. Basic WWW::OAuth::Request::Basic contains the request attributes directly, for user agents such as HTTP::Tiny that do not use request objects. HTTP_Request WWW::OAuth::Request::HTTP_Request wraps a HTTP::Request object, which is compatible with several user agents including LWP::UserAgent, HTTP::Thin, and Net::Async::HTTP. Mojo WWW::OAuth::Request::Mojo wraps a Mojo::Message::Request object, which is used by Mojo::UserAgent via Mojo::Transaction. BUGS Report any issues on the public bugtracker. This module was developed primarily to interface with the Twitter API, which is now functionally unusable, so further development of this module is unlikely without another use case. AUTHOR Dan Book COPYRIGHT AND LICENSE This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) SEE ALSO Net::OAuth, Mojolicious::Plugin::OAuth2 Changes100644001750001750 370314741612716 14607 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.0021.002 2025-01-14 20:56:30 EST - Simpler generation of nonce from random bytes. 1.001 2025-01-06 04:15:01 EST - Use Crypt::URandom to generate nonce with stronger randomness. 1.000 2018-09-17 19:08:03 EDT - Support 'realm' protocol parameter (not included in signature base string) as per RFC 5849. - Added request_with_p method to Mojo request wrapper. 0.006 2016-12-09 23:46:25 EST - Improve the default oauth_nonce algorithm to provide better uniqueness - Removed dependency on MIME::Base64 - The signature_method attribute now accepts 'RSA-SHA1' and custom signature methods, but for these signature methods a coderef must be provided to the new signer attribute. RSA-SHA1 signers can no longer be passed directly as signature_method. 0.005 2016-12-08 22:49:18 EST - No longer move parameters starting with oauth_ to the Authorization header from the query or body parameters; the only modification to the request is to add the Authorization header itself, as per the spec. Custom oauth_ parameters should be passed directly to the authenticate or authorization_header methods rather than in query or body parameters, so they can be properly sent in the header. - Removed the remove_query_params and remove_body_params methods from the WWW::OAuth::Request role as they are no longer needed. - Added authorization_header method that returns the Authorization header value instead of setting it in the request. 0.004 2016-11-23 17:29:28 EST - Use WWW::Form::UrlEncoded to implement utility functions form_urlencode and form_urldecode 0.003 2016-05-19 00:35:49 EDT - Use Test::TCP for author tests - Use Test::Needs in place of Test::Requires for optional test dependencies 0.002 2016-01-31 23:22:14 EST - Allow RSA-SHA1 signature method to be specified as a coderef - Update dependencies and checks to fix test failures 0.001 2016-01-31 02:53:01 EST - First release LICENSE100644001750001750 2151514741612716 14342 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) The Artistic License 2.0 Copyright (c) 2000-2006, The Perl Foundation. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble This license establishes the terms under which a given free software Package may be copied, modified, distributed, and/or redistributed. The intent is that the Copyright Holder maintains some artistic control over the development of that Package while still keeping the Package available as open source and free software. You are always permitted to make arrangements wholly outside of this license directly with the Copyright Holder of a given Package. If the terms of this license do not permit the full use that you propose to make of the Package, you should contact the Copyright Holder and seek a different licensing arrangement. Definitions "Copyright Holder" means the individual(s) or organization(s) named in the copyright notice for the entire Package. "Contributor" means any party that has contributed code or other material to the Package, in accordance with the Copyright Holder's procedures. "You" and "your" means any person who would like to copy, distribute, or modify the Package. "Package" means the collection of files distributed by the Copyright Holder, and derivatives of that collection and/or of those files. A given Package may consist of either the Standard Version, or a Modified Version. "Distribute" means providing a copy of the Package or making it accessible to anyone else, or in the case of a company or organization, to others outside of your company or organization. "Distributor Fee" means any fee that you charge for Distributing this Package or providing support for this Package to another party. It does not mean licensing fees. "Standard Version" refers to the Package if it has not been modified, or has been modified only in ways explicitly requested by the Copyright Holder. "Modified Version" means the Package, if it has been changed, and such changes were not explicitly requested by the Copyright Holder. "Original License" means this Artistic License as Distributed with the Standard Version of the Package, in its current version or as it may be modified by The Perl Foundation in the future. "Source" form means the source code, documentation source, and configuration files for the Package. "Compiled" form means the compiled bytecode, object code, binary, or any other form resulting from mechanical transformation or translation of the Source form. Permission for Use and Modification Without Distribution (1) You are permitted to use the Standard Version and create and use Modified Versions for any purpose without restriction, provided that you do not Distribute the Modified Version. Permissions for Redistribution of the Standard Version (2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers. At your discretion, such verbatim copies may or may not include a Compiled form of the Package. (3) You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder. The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License. Distribution of Modified Versions of the Package as Source (4) You may Distribute your Modified Version as Source (either gratis or for a Distributor Fee, and with or without a Compiled form of the Modified Version) provided that you clearly document how it differs from the Standard Version, including, but not limited to, documenting any non-standard features, executables, or modules, and provided that you do at least ONE of the following: (a) make the Modified Version available to the Copyright Holder of the Standard Version, under the Original License, so that the Copyright Holder may include your modifications in the Standard Version. (b) ensure that installation of your Modified Version does not prevent the user installing or running the Standard Version. In addition, the Modified Version must bear a name that is different from the name of the Standard Version. (c) allow anyone who receives a copy of the Modified Version to make the Source form of the Modified Version available to others under (i) the Original License or (ii) a license that permits the licensee to freely copy, modify and redistribute the Modified Version using the same licensing terms that apply to the copy that the licensee received, and requires that the Source form of the Modified Version, and of any works derived from it, be made freely available in that license fees are prohibited but Distributor Fees are allowed. Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source (5) You may Distribute Compiled forms of the Standard Version without the Source, provided that you include complete instructions on how to get the Source of the Standard Version. Such instructions must be valid at the time of your distribution. If these instructions, at any time while you are carrying out such distribution, become invalid, you must provide new instructions on demand or cease further distribution. If you provide valid instructions or cease distribution within thirty days after you become aware that the instructions are invalid, then you do not forfeit any of your rights under this license. (6) You may Distribute a Modified Version in Compiled form without the Source, provided that you comply with Section 4 with respect to the Source of the Modified Version. Aggregating or Linking the Package (7) You may aggregate the Package (either the Standard Version or Modified Version) with other packages and Distribute the resulting aggregation provided that you do not charge a licensing fee for the Package. Distributor Fees are permitted, and licensing fees for other components in the aggregation are permitted. The terms of this license apply to the use and Distribution of the Standard or Modified Versions as included in the aggregation. (8) You are permitted to link Modified and Standard Versions with other works, to embed the Package in a larger work of your own, or to build stand-alone binary or bytecode versions of applications that include the Package, and Distribute the result without restriction, provided the result does not expose a direct interface to the Package. Items That are Not Considered Part of a Modified Version (9) Works (including, but not limited to, modules and scripts) that merely extend or make use of the Package, do not, by themselves, cause the Package to be a Modified Version. In addition, such works are not considered parts of the Package itself, and are not subject to the terms of this license. General Provisions (10) Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license. (11) If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license. (12) This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder. (13) This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed. (14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. INSTALL100644001750001750 452614741612716 14351 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002This is the Perl distribution WWW-OAuth. Installing WWW-OAuth is straightforward. ## Installation with cpanm If you have cpanm, you only need one line: % cpanm WWW::OAuth If it does not have permission to install modules to the current perl, cpanm will automatically set up and install to a local::lib in your home directory. See the local::lib documentation (https://metacpan.org/pod/local::lib) for details on enabling it in your environment. ## Installing with the CPAN shell Alternatively, if your CPAN shell is set up, you should just be able to do: % cpan WWW::OAuth ## Manual installation As a last resort, you can manually install it. If you have not already downloaded the release tarball, you can find the download link on the module's MetaCPAN page: https://metacpan.org/pod/WWW::OAuth Untar the tarball, install configure prerequisites (see below), then build it: % perl Makefile.PL % make && make test Then install it: % make install On Windows platforms, you should use `dmake` or `nmake`, instead of `make`. If your perl is system-managed, you can create a local::lib in your home directory to install modules to. For details, see the local::lib documentation: https://metacpan.org/pod/local::lib The prerequisites of this distribution will also have to be installed manually. The prerequisites are listed in one of the files: `MYMETA.yml` or `MYMETA.json` generated by running the manual build process described above. ## Configure Prerequisites This distribution requires other modules to be installed before this distribution's installer can be run. They can be found under the "configure_requires" key of META.yml or the "{prereqs}{configure}{requires}" key of META.json. ## Other Prerequisites This distribution may require additional modules to be installed after running Makefile.PL. Look for prerequisites in the following phases: * to run make, PHASE = build * to use the module code itself, PHASE = runtime * to run tests, PHASE = test They can all be found in the "PHASE_requires" key of MYMETA.yml or the "{prereqs}{PHASE}{requires}" key of MYMETA.json. ## Documentation WWW-OAuth documentation is available as POD. You can run `perldoc` from a shell to read the documentation: % perldoc WWW::OAuth For more information on installing Perl modules via CPAN, please see: https://www.cpan.org/modules/INSTALL.html dist.ini100644001750001750 42114741612716 14732 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002name = WWW-OAuth author = Dan Book license = Artistic_2_0 copyright_holder = Dan Book copyright_year = 2015 [@Author::DBOOK] :version = v1.0.2 pod_tests = 1 Test::ReportPrereqs.include[0] = Mojolicious Test::ReportPrereqs.include[1] = HTTP::Request t000755001750001750 014741612716 13414 5ustar00grinnzgrinnz000000000000WWW-OAuth-1.002util.t100644001750001750 714214741612716 14722 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002/tuse strict; use warnings; use utf8; use Scalar::Util 'blessed', 'refaddr'; use Test::More; use WWW::OAuth::Request::Basic; use WWW::OAuth::Util 'form_urldecode', 'form_urlencode', 'oauth_request'; is form_urlencode([]), '', 'empty form'; is form_urlencode({}), '', 'empty form'; is form_urlencode([undef, 'foo']), '=foo', 'empty key'; is form_urlencode([foo => undef]), 'foo=', 'empty value'; is_deeply form_urldecode(''), [], 'empty form'; is_deeply form_urldecode('='), ['', ''], 'empty key and value'; is_deeply form_urldecode('=foo'), ['', 'foo'], 'empty key'; is_deeply form_urldecode('foo'), ['foo', ''], 'empty value'; is_deeply form_urldecode('&'), ['', '', '', ''], 'empty keys and values'; is_deeply form_urldecode('foo&=bar&'), ['foo', '', '', 'bar', '', ''], 'weird form'; my ($decoded, $encoded); $decoded = [foo => ['☃', '❤'], '❤' => 'a b c', baz => 0]; $encoded = form_urlencode $decoded; is $encoded, 'foo=%E2%98%83&foo=%E2%9D%A4&%E2%9D%A4=a+b+c&baz=0', 'form urlencoded correctly'; $decoded = form_urldecode $encoded; is_deeply $decoded, ['foo', '☃', 'foo', '❤', '❤', 'a b c', 'baz', '0'], 'form urldecoded correctly'; $decoded = {foo => [2, 1], '❤' => 'a ☃ c', baz => 0}; $encoded = form_urlencode $decoded; is $encoded, 'baz=0&foo=2&foo=1&%E2%9D%A4=a+%E2%98%83+c', 'form urlencoded correctly'; $decoded = form_urldecode $encoded; is_deeply $decoded, ['baz', '0', 'foo', '2', 'foo', '1', '❤', 'a ☃ c'], 'form urldecoded correctly'; {package WWW::OAuth::Fake::HTTP::Request; sub new { bless {}, shift } sub isa { $_[1] eq 'HTTP::Request' ? 1 : 0 } } {package WWW::OAuth::Fake::Mojo::Request; sub new { bless {}, shift } sub isa { $_[1] eq 'Mojo::Message::Request' ? 1 : 0 } } my $existing = WWW::OAuth::Request::Basic->new; my $existing_req = oauth_request($existing); is refaddr($existing_req), refaddr($existing), 'same container returned'; my $basic_href = {method => 'FOO', url => 'http://example.com', content => 'bar'}; my $basic_req = oauth_request($basic_href); is blessed($basic_req), 'WWW::OAuth::Request::Basic', 'created Basic request object'; is $basic_req->method, 'FOO', 'method is FOO'; is $basic_req->url, 'http://example.com', 'url is http://example.com'; is $basic_req->content, 'bar', 'content is bar'; $basic_req = oauth_request(Basic => $basic_href); is blessed($basic_req), 'WWW::OAuth::Request::Basic', 'created Basic request object'; is $basic_req->method, 'FOO', 'method is FOO'; is $basic_req->url, 'http://example.com', 'url is http://example.com'; is $basic_req->content, 'bar', 'content is bar'; my $http_request = WWW::OAuth::Fake::HTTP::Request->new; my $http_request_req = oauth_request($http_request); is blessed($http_request_req), 'WWW::OAuth::Request::HTTP_Request', 'created HTTP_Request request object'; is refaddr($http_request_req->request), refaddr($http_request), 'same request object'; $http_request_req = oauth_request(HTTP_Request => $http_request); is blessed($http_request_req), 'WWW::OAuth::Request::HTTP_Request', 'created HTTP_Request request object'; is refaddr($http_request_req->request), refaddr($http_request), 'same request object'; my $mojo_request = WWW::OAuth::Fake::Mojo::Request->new; my $mojo_request_req = oauth_request($mojo_request); is blessed($mojo_request_req), 'WWW::OAuth::Request::Mojo', 'created Mojo request object'; is refaddr($mojo_request_req->request), refaddr($mojo_request), 'same request object'; $mojo_request_req = oauth_request(Mojo => $mojo_request); is blessed($mojo_request_req), 'WWW::OAuth::Request::Mojo', 'created Mojo request object'; is refaddr($mojo_request_req->request), refaddr($mojo_request), 'same request object'; done_testing; META.yml100644001750001750 334714741612716 14571 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002--- abstract: 'Portable OAuth 1.0 authentication' author: - 'Dan Book ' build_requires: Data::Dumper: '0' ExtUtils::MakeMaker: '0' File::Spec: '0' JSON::PP: '0' Test::More: '0.88' Test::Needs: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.032, CPAN::Meta::Converter version 2.150010' license: artistic_2 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: WWW-OAuth no_index: directory: - eg - examples - inc - share - t - xt provides: WWW::OAuth: file: lib/WWW/OAuth.pm version: '1.002' WWW::OAuth::Request: file: lib/WWW/OAuth/Request.pm version: '1.002' WWW::OAuth::Request::Basic: file: lib/WWW/OAuth/Request/Basic.pm version: '1.002' WWW::OAuth::Request::HTTP_Request: file: lib/WWW/OAuth/Request/HTTP_Request.pm version: '1.002' WWW::OAuth::Request::Mojo: file: lib/WWW/OAuth/Request/Mojo.pm version: '1.002' WWW::OAuth::Util: file: lib/WWW/OAuth/Util.pm version: '1.002' recommends: WWW::Form::UrlEncoded::XS: '0.23' requires: Carp: '0' Class::Tiny::Chained: '0' Crypt::URandom: '0.37' Digest::SHA: '0' List::Util: '1.33' Module::Runtime: '0' Role::Tiny: '2.000000' Scalar::Util: '0' URI: '1.28' URI::Escape: '3.26' WWW::Form::UrlEncoded: '0.23' perl: '5.008001' resources: bugtracker: https://github.com/Grinnz/WWW-OAuth/issues homepage: https://github.com/Grinnz/WWW-OAuth repository: https://github.com/Grinnz/WWW-OAuth.git version: '1.002' x_contributors: - 'Dan Book ' x_generated_by_perl: v5.40.0 x_serialization_backend: 'YAML::Tiny version 1.74' x_spdx_expression: Artistic-2.0 MANIFEST100644001750001750 105714741612716 14445 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.032. CONTRIBUTING.md Changes INSTALL LICENSE MANIFEST META.json META.yml Makefile.PL README dist.ini lib/WWW/OAuth.pm lib/WWW/OAuth/Request.pm lib/WWW/OAuth/Request/Basic.pm lib/WWW/OAuth/Request/HTTP_Request.pm lib/WWW/OAuth/Request/Mojo.pm lib/WWW/OAuth/Util.pm prereqs.yml t/00-report-prereqs.dd t/00-report-prereqs.t t/oauth.t t/request.t t/request_basic.t t/request_http_request.t t/request_mojo.t t/util.t xt/author/pod-coverage.t xt/author/pod-syntax.t xt/author/request_ua.t oauth.t100644001750001750 2030714741612716 15103 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002/tuse strict; use warnings; use Test::More; use Data::Dumper 'Dumper'; use Digest::SHA 'hmac_sha1_base64'; use JSON::PP 'decode_json'; use URI; use URI::Escape 'uri_escape_utf8', 'uri_unescape'; use WWW::OAuth; use WWW::OAuth::Util 'form_urldecode', 'form_urlencode', 'oauth_request'; my $api_key = $ENV{TWITTER_API_KEY}; my $api_secret = $ENV{TWITTER_API_SECRET}; my $token = $ENV{TWITTER_ACCESS_TOKEN}; my $token_secret = $ENV{TWITTER_ACCESS_SECRET}; my $oauth_base_url = 'https://api.twitter.com/oauth/'; my $api_base_url = 'https://api.twitter.com/1.1/'; my $test_online; if ($ENV{AUTHOR_TESTING} and defined $api_key and defined $api_secret and defined $token and defined $token_secret) { note 'Running online test for Twitter OAuth 1.0'; $test_online = 1; require HTTP::Tiny; HTTP::Tiny->VERSION('0.014'); } else { note 'Running offline test for Twitter OAuth 1.0; set AUTHOR_TESTING and TWITTER_API_KEY/TWITTER_API_SECRET/TWITTER_ACCESS_TOKEN/TWITTER_ACCESS_SECRET for online test'; $api_key = 'foo'; $api_secret = 'bar'; $token = 'baz'; $token_secret = 'ban'; } # OAuth token request my $oauth_request_url = $oauth_base_url . 'request_token'; my $oauth_request = _request(POST => $oauth_request_url); my $auth = WWW::OAuth->new(client_id => $api_key, client_secret => $api_secret); my $auth_header = $auth->authorization_header($oauth_request, { oauth_callback => 'oob' }); like $auth_header, qr/^OAuth oauth_/, 'Formed Authorization header'; $oauth_request->header(Authorization => $auth_header); is $auth_header, $oauth_request->header('Authorization'), 'Authorization header has been set'; my $oauth_params = _parse_oauth_header($auth_header); is $oauth_params->{oauth_consumer_key}, $api_key, 'oauth_consumer_key is set to API key'; ok defined($oauth_params->{oauth_nonce}), 'oauth_nonce is set'; is $oauth_params->{oauth_signature_method}, 'HMAC-SHA1', 'oauth_signature_method is set to HMAC-SHA1'; ok defined($oauth_params->{oauth_timestamp}), 'oauth_timestamp is set'; is $oauth_params->{oauth_version}, '1.0', 'oauth_version is set to 1.0'; ok defined($oauth_params->{oauth_signature}), 'oauth_signature is set'; is $oauth_params->{oauth_callback}, 'oob', 'oauth_callback is set to "oob"'; ok !defined($oauth_params->{oauth_token}), 'oauth_token is not set'; my $test_signature = _test_signature('POST', $oauth_request_url, $oauth_params, $api_secret); is $test_signature, $oauth_params->{oauth_signature}, 'signature formed correctly'; my $response; if ($test_online) { my $res = $oauth_request->request_with(HTTP::Tiny->new); ok $res->{success}, 'OAuth request successful' or diag Dumper $res; $response = $res->{content}; } else { $response = q{oauth_token=aaaaaaaaaaaaaaaaaaaaaaaaaaa&oauth_token_secret=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&oauth_callback_confirmed=true}; } my %response_params = @{form_urldecode($response)}; is $response_params{oauth_callback_confirmed}, 'true', 'OAuth callback was confirmed'; ok defined(my $request_token = $response_params{oauth_token}), 'got request token'; ok defined(my $request_secret = $response_params{oauth_token_secret}), 'got request secret'; # Verify credentials request my $verify_url = $api_base_url . 'account/verify_credentials.json'; my $verify_request = _request(GET => $verify_url); $auth->token($token); $auth->token_secret($token_secret); $auth->authenticate($verify_request, { realm => 'foo' }); $auth_header = $verify_request->header('Authorization'); ok defined $auth_header, 'Authorization header has been set'; $oauth_params = _parse_oauth_header($auth_header); is $oauth_params->{oauth_consumer_key}, $api_key, 'oauth_consumer_key is set to API key'; ok defined($oauth_params->{oauth_nonce}), 'oauth_nonce is set'; is $oauth_params->{oauth_signature_method}, 'HMAC-SHA1', 'oauth_signature_method is set to HMAC-SHA1'; ok defined($oauth_params->{oauth_timestamp}), 'oauth_timestamp is set'; is $oauth_params->{oauth_version}, '1.0', 'oauth_version is set to 1.0'; ok defined($oauth_params->{oauth_signature}), 'oauth_signature is set'; is $oauth_params->{oauth_token}, $token, 'oauth_token is set to API access token'; is $oauth_params->{realm}, 'foo', 'realm is set'; $test_signature = _test_signature('GET', $verify_url, $oauth_params, $api_secret, $token_secret); is $test_signature, $oauth_params->{oauth_signature}, 'signature formed correctly'; if ($test_online) { my $res = $verify_request->request_with(HTTP::Tiny->new); ok $res->{success}, 'OAuth request successful' or diag Dumper $res; $response = $res->{content}; } else { $response = <<'JSON_RESPONSE'; {"id":36885345,"id_str":"36885345","name":"Grinnz","screen_name":"TheGrinnz","location":"CT","description":"nerrrrrd","url":"http:\/\/t.co\/IomLjiiycH","entities":{"url":{"urls":[{"url":"http:\/\/t.co\/IomLjiiycH","expanded_url":"http:\/\/grinnz.net","display_url":"grinnz.net","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":73,"friends_count":74,"listed_count":3,"created_at":"Fri May 01 04:45:52 +0000 2009","favourites_count":0,"utc_offset":-18000,"time_zone":"Quito","geo_enabled":true,"verified":false,"statuses_count":365,"lang":"en","status":{"created_at":"Thu Aug 14 03:05:35 +0000 2014","id":499753673501990912,"id_str":"499753673501990912","text":"@GermanFatass vpssd down, at least one irc server still up","source":"\u003ca href=\"http:\/\/twitter.com\" rel=\"nofollow\"\u003eTwitter Web Client\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":499752682178224128,"in_reply_to_status_id_str":"499752682178224128","in_reply_to_user_id":66307969,"in_reply_to_user_id_str":"66307969","in_reply_to_screen_name":"GermanFatass","geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[],"symbols":[],"user_mentions":[{"screen_name":"GermanFatass","name":"John ","id":66307969,"id_str":"66307969","indices":[0,13]}],"urls":[]},"favorited":false,"retweeted":false,"lang":"en"},"contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"C6E2EE","profile_background_image_url":"http:\/\/abs.twimg.com\/images\/themes\/theme2\/bg.gif","profile_background_image_url_https":"https:\/\/abs.twimg.com\/images\/themes\/theme2\/bg.gif","profile_background_tile":false,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/221712898\/tux_redhat80_normal.png","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/221712898\/tux_redhat80_normal.png","profile_link_color":"1F98C7","profile_sidebar_border_color":"C6E2EE","profile_sidebar_fill_color":"DAECF4","profile_text_color":"663B12","profile_use_background_image":true,"has_extended_profile":false,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false} JSON_RESPONSE } my $response_data = decode_json $response; ok defined($response_data->{id_str}), 'Twitter ID returned in response'; sub _request { my ($method, $url, $params) = @_; my %req = (method => $method, url => $url); if (defined $params) { if ($method eq 'GET' or $method eq 'HEAD') { my $uri = URI->new($url); $uri->query_form($params); $req{url} = $uri->as_string; } else { $req{content} = form_urlencode($params); $req{headers}{'content-type'} = 'application/x-www-form-urlencoded'; } } return oauth_request(Basic => \%req); } sub _parse_oauth_header { my $auth_header = shift; return {} unless defined $auth_header; my %oauth_params; while ($auth_header =~ m/(\w+)="(.+?)"/g) { $oauth_params{$1} = uri_unescape $2; } return \%oauth_params; } sub _test_signature { my ($method, $request_url, $oauth_params, $client_secret, $token_secret) = @_; my $params_str = join '&', map { $_ . '=' . uri_escape_utf8($oauth_params->{$_}) } grep { defined $oauth_params->{$_} } qw(oauth_callback oauth_consumer_key oauth_nonce oauth_signature_method oauth_timestamp oauth_token oauth_version); my $base_str = uc($method) . '&' . uri_escape_utf8($request_url) . '&' . uri_escape_utf8($params_str); my $signing_key = uri_escape_utf8($client_secret) . '&'; $signing_key .= uri_escape_utf8($token_secret) if defined $token_secret; my $test_signature = hmac_sha1_base64($base_str, $signing_key); $test_signature .= '='x(4 - length($test_signature) % 4) if length($test_signature) % 4; return $test_signature; } done_testing; META.json100644001750001750 644114741612716 14737 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002{ "abstract" : "Portable OAuth 1.0 authentication", "author" : [ "Dan Book " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.032, CPAN::Meta::Converter version 2.150010", "license" : [ "artistic_2" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "WWW-OAuth", "no_index" : { "directory" : [ "eg", "examples", "inc", "share", "t", "xt" ] }, "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "develop" : { "recommends" : { "Mojolicious" : "7.54" }, "requires" : { "HTTP::Request" : "0", "HTTP::Tiny" : "0.014", "LWP::UserAgent" : "0", "Pod::Coverage::TrustPod" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08", "Test::TCP" : "0" } }, "runtime" : { "recommends" : { "WWW::Form::UrlEncoded::XS" : "0.23" }, "requires" : { "Carp" : "0", "Class::Tiny::Chained" : "0", "Crypt::URandom" : "0.37", "Digest::SHA" : "0", "List::Util" : "1.33", "Module::Runtime" : "0", "Role::Tiny" : "2.000000", "Scalar::Util" : "0", "URI" : "1.28", "URI::Escape" : "3.26", "WWW::Form::UrlEncoded" : "0.23", "perl" : "5.008001" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "Data::Dumper" : "0", "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "JSON::PP" : "0", "Test::More" : "0.88", "Test::Needs" : "0" } } }, "provides" : { "WWW::OAuth" : { "file" : "lib/WWW/OAuth.pm", "version" : "1.002" }, "WWW::OAuth::Request" : { "file" : "lib/WWW/OAuth/Request.pm", "version" : "1.002" }, "WWW::OAuth::Request::Basic" : { "file" : "lib/WWW/OAuth/Request/Basic.pm", "version" : "1.002" }, "WWW::OAuth::Request::HTTP_Request" : { "file" : "lib/WWW/OAuth/Request/HTTP_Request.pm", "version" : "1.002" }, "WWW::OAuth::Request::Mojo" : { "file" : "lib/WWW/OAuth/Request/Mojo.pm", "version" : "1.002" }, "WWW::OAuth::Util" : { "file" : "lib/WWW/OAuth/Util.pm", "version" : "1.002" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/Grinnz/WWW-OAuth/issues" }, "homepage" : "https://github.com/Grinnz/WWW-OAuth", "repository" : { "type" : "git", "url" : "https://github.com/Grinnz/WWW-OAuth.git", "web" : "https://github.com/Grinnz/WWW-OAuth" } }, "version" : "1.002", "x_contributors" : [ "Dan Book " ], "x_generated_by_perl" : "v5.40.0", "x_serialization_backend" : "Cpanel::JSON::XS version 4.38", "x_spdx_expression" : "Artistic-2.0" } prereqs.yml100644001750001750 111214741612716 15510 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002runtime: requires: perl: '5.008001' Carp: 0 Class::Tiny::Chained: 0 Crypt::URandom: '0.37' Digest::SHA: 0 List::Util: '1.33' Module::Runtime: 0 Role::Tiny: '2.000000' Scalar::Util: 0 URI: '1.28' URI::Escape: '3.26' WWW::Form::UrlEncoded: '0.23' recommends: WWW::Form::UrlEncoded::XS: '0.23' test: requires: Data::Dumper: 0 JSON::PP: 0 Test::More: '0.88' Test::Needs: 0 develop: requires: HTTP::Request: 0 HTTP::Tiny: '0.014' LWP::UserAgent: 0 Test::TCP: 0 recommends: Mojolicious: '7.54' request.t100644001750001750 172014741612716 15431 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002/tuse strict; use warnings; use utf8; {package WWW::OAuth::Request::Test; use Class::Tiny::Chained 'method', 'url', 'content'; use Role::Tiny::With; with 'WWW::OAuth::Request'; sub content_is_form { 1 } sub header { } sub request_with { } } use Test::More; use WWW::OAuth::Util 'form_urlencode'; my $req = WWW::OAuth::Request::Test->new(url => 'http::example.com'); is_deeply $req->query_pairs, [], 'no query parameters'; is_deeply $req->body_pairs, [], 'no body parameters'; $req->url('http://example.com?' . form_urlencode [foo => ['☃', '❤'], '❤' => 'a b c', baz => 0, bar => '☃']); is_deeply $req->query_pairs, ['foo', '☃', 'foo', '❤', '❤', 'a b c', 'baz', '0', 'bar', '☃'], 'URL has query parameters'; $req->content(form_urlencode [foo => ['☃', '❤'], '❤' => 'a b c', baz => 0, bar => '☃']); is_deeply $req->body_pairs, ['foo', '☃', 'foo', '❤', '❤', 'a b c', 'baz', '0', 'bar', '☃'], 'Request has body parameters'; done_testing; Makefile.PL100644001750001750 345214741612716 15267 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.032. use strict; use warnings; use 5.008001; use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "Portable OAuth 1.0 authentication", "AUTHOR" => "Dan Book ", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "WWW-OAuth", "LICENSE" => "artistic_2", "MIN_PERL_VERSION" => "5.008001", "NAME" => "WWW::OAuth", "PREREQ_PM" => { "Carp" => 0, "Class::Tiny::Chained" => 0, "Crypt::URandom" => "0.37", "Digest::SHA" => 0, "List::Util" => "1.33", "Module::Runtime" => 0, "Role::Tiny" => "2.000000", "Scalar::Util" => 0, "URI" => "1.28", "URI::Escape" => "3.26", "WWW::Form::UrlEncoded" => "0.23" }, "TEST_REQUIRES" => { "Data::Dumper" => 0, "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "JSON::PP" => 0, "Test::More" => "0.88", "Test::Needs" => 0 }, "VERSION" => "1.002", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "Carp" => 0, "Class::Tiny::Chained" => 0, "Crypt::URandom" => "0.37", "Data::Dumper" => 0, "Digest::SHA" => 0, "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "JSON::PP" => 0, "List::Util" => "1.33", "Module::Runtime" => 0, "Role::Tiny" => "2.000000", "Scalar::Util" => 0, "Test::More" => "0.88", "Test::Needs" => 0, "URI" => "1.28", "URI::Escape" => "3.26", "WWW::Form::UrlEncoded" => "0.23" ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); CONTRIBUTING.md100644001750001750 1055514741612716 15570 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002# HOW TO CONTRIBUTE Thank you for considering contributing to this distribution. This file contains instructions that will help you work with the source code. The distribution is managed with [Dist::Zilla](https://metacpan.org/pod/Dist::Zilla). This means that many of the usual files you might expect are not in the repository, but are generated at release time. Some generated files are kept in the repository as a convenience (e.g. Build.PL/Makefile.PL and META.json). Generally, **you do not need Dist::Zilla to contribute patches**. You may need Dist::Zilla to create a tarball. See below for guidance. ## Getting dependencies If you have App::cpanminus 1.6 or later installed, you can use [cpanm](https://metacpan.org/pod/cpanm) to satisfy dependencies like this: $ cpanm --installdeps --with-develop . You can also run this command (or any other cpanm command) without installing App::cpanminus first, using the fatpacked `cpanm` script via curl or wget: $ curl -L https://cpanmin.us | perl - --installdeps --with-develop . $ wget -qO - https://cpanmin.us | perl - --installdeps --with-develop . Otherwise, look for either a `cpanfile`, `prereqs.json`/`prereqs.yml`, or `META.json` file for a list of dependencies to satisfy. ## Running tests You can run tests directly using the `prove` tool: $ prove -l $ prove -lv t/some_test_file.t For most of my distributions, `prove` is entirely sufficient for you to test any patches you have. I use `prove` for 99% of my testing during development. ## Code style and tidying Please try to match any existing coding style. If there is a `.perltidyrc` file, please install Perl::Tidy and use perltidy before submitting patches. ## Installing and using Dist::Zilla [Dist::Zilla](https://metacpan.org/pod/Dist::Zilla) is a very powerful authoring tool, optimized for maintaining a large number of distributions with a high degree of automation, but it has a large dependency chain, a bit of a learning curve and requires a number of author-specific plugins. To install it from CPAN, I recommend one of the following approaches for the quickest installation: # using CPAN.pm, but bypassing non-functional pod tests $ cpan TAP::Harness::Restricted $ PERL_MM_USE_DEFAULT=1 HARNESS_CLASS=TAP::Harness::Restricted cpan Dist::Zilla # using cpanm, bypassing *all* tests $ cpanm -n Dist::Zilla In either case, it's probably going to take about 10 minutes. Go for a walk, go get a cup of your favorite beverage, take a bathroom break, or whatever. When you get back, Dist::Zilla should be ready for you. Then you need to install any plugins specific to this distribution: $ dzil authordeps --missing | cpanm You can use Dist::Zilla to install the distribution's dependencies if you haven't already installed them with cpanm: $ dzil listdeps --missing --develop | cpanm You can instead combine these two steps into one command by installing Dist::Zilla::App::Command::installdeps then running: $ dzil installdeps Once everything is installed, here are some dzil commands you might try: $ dzil build $ dzil test $ dzil regenerate You can learn more about Dist::Zilla at http://dzil.org/ ## Other notes This distribution maintains the generated `META.json` and either `Makefile.PL` or `Build.PL` in the repository. This allows two things: [Travis CI](https://travis-ci.org/) can build and test the distribution without requiring Dist::Zilla, and the distribution can be installed directly from Github or a local git repository using `cpanm` for testing (again, not requiring Dist::Zilla). $ cpanm git://github.com/Author/Distribution-Name.git $ cd Distribution-Name; cpanm . Contributions are preferred in the form of a Github pull request. See [Using pull requests](https://help.github.com/articles/using-pull-requests/) for further information. You can use the Github issue tracker to report issues without an accompanying patch. # CREDITS This file was adapted from an initial `CONTRIBUTING.mkdn` file from David Golden under the terms of the [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/), with inspiration from the contributing documents from [Dist::Zilla::Plugin::Author::KENTNL::CONTRIBUTING](https://metacpan.org/pod/Dist::Zilla::Plugin::Author::KENTNL::CONTRIBUTING) and [Dist::Zilla::PluginBundle::Author::ETHER](https://metacpan.org/pod/Dist::Zilla::PluginBundle::Author::ETHER). WWW000755001750001750 014741612716 14403 5ustar00grinnzgrinnz000000000000WWW-OAuth-1.002/libOAuth.pm100644001750001750 3011414741612716 16140 0ustar00grinnzgrinnz000000000000WWW-OAuth-1.002/lib/WWWpackage WWW::OAuth; use strict; use warnings; my %default_signer = ( 'PLAINTEXT' => \&_signer_plaintext, 'HMAC-SHA1' => \&_signer_hmac_sha1, ); use Class::Tiny::Chained qw(client_id client_secret token token_secret), { signature_method => 'HMAC-SHA1', signer => sub { $default_signer{$_[0]->signature_method} }, }; use Carp 'croak'; use Crypt::URandom 'urandom_ub'; use Digest::SHA 'hmac_sha1_base64'; use List::Util 'all', 'pairs', 'pairgrep'; use Scalar::Util 'blessed'; use URI; use URI::Escape 'uri_escape_utf8'; use WWW::OAuth::Util 'oauth_request'; our $VERSION = '1.002'; sub authenticate { my $self = shift; my @req_args = ref $_[0] ? shift() : (shift, shift); my $req = oauth_request(@req_args); my $auth_header = $self->authorization_header($req, @_); $req->header(Authorization => $auth_header); return $req; } sub authorization_header { my $self = shift; my @req_args = ref $_[0] ? shift() : (shift, shift); my $req = oauth_request(@req_args); my $extra_params = shift; my ($client_id, $client_secret, $token, $token_secret, $signature_method, $signer) = ($self->client_id, $self->client_secret, $self->token, $self->token_secret, $self->signature_method, $self->signer); croak 'Client ID and secret are required to generate authorization header' unless defined $client_id and defined $client_secret; croak "Signer is required for signature method $signature_method" unless defined $signer; if ($signature_method eq 'RSA-SHA1' and blessed $signer) { my $signer_obj = $signer; croak 'Signer for RSA-SHA1 must have "sign" method' unless $signer_obj->can('sign'); $signer = sub { $signer_obj->sign($_[0]) }; } croak "Signer for $signature_method must be a coderef" unless !blessed $signer and ref $signer eq 'CODE'; my %oauth_params = ( oauth_consumer_key => $client_id, oauth_nonce => _nonce(), oauth_signature_method => $signature_method, oauth_timestamp => time, oauth_version => '1.0', ); $oauth_params{oauth_token} = $token if defined $token; # Extra parameters passed to authenticate() if (defined $extra_params) { croak 'OAuth parameters must be specified as a hashref' unless ref $extra_params eq 'HASH'; croak 'OAuth parameters other than "realm" must all begin with "oauth_"' unless all { $_ eq 'realm' or m/^oauth_/ } keys %$extra_params; %oauth_params = (%oauth_params, %$extra_params); } # This parameter is not allowed when creating the signature delete $oauth_params{oauth_signature}; # Don't bother to generate signature base string for PLAINTEXT method my $base_str = $signature_method eq 'PLAINTEXT' ? '' : _signature_base_string($req, \%oauth_params); $oauth_params{oauth_signature} = $signer->($base_str, $client_secret, $token_secret); my $auth_str = join ', ', map { $_ . '="' . uri_escape_utf8($oauth_params{$_}) . '"' } sort keys %oauth_params; return "OAuth $auth_str"; } sub _nonce { scalar unpack 'H*', urandom_ub(20) } # random hex string sub _signer_plaintext { my ($base_str, $client_secret, $token_secret) = @_; $token_secret = '' unless defined $token_secret; return uri_escape_utf8($client_secret) . '&' . uri_escape_utf8($token_secret); } sub _signer_hmac_sha1 { my ($base_str, $client_secret, $token_secret) = @_; $token_secret = '' unless defined $token_secret; my $signing_key = uri_escape_utf8($client_secret) . '&' . uri_escape_utf8($token_secret); my $digest = hmac_sha1_base64($base_str, $signing_key); $digest .= '='x(4 - length($digest) % 4) if length($digest) % 4; # Digest::SHA does not pad Base64 digests return $digest; } sub _signature_base_string { my ($req, $oauth_params) = @_; my @all_params = @{$req->query_pairs}; push @all_params, map { ($_ => $oauth_params->{$_}) } grep { $_ ne 'realm' } keys %$oauth_params; push @all_params, @{$req->body_pairs} if $req->content_is_form; my @pairs = pairs map { uri_escape_utf8 $_ } @all_params; @pairs = sort { ($a->[0] cmp $b->[0]) or ($a->[1] cmp $b->[1]) } @pairs; my $params_str = join '&', map { $_->[0] . '=' . $_->[1] } @pairs; my $base_url = URI->new($req->url); $base_url->query(undef); $base_url->fragment(undef); return uc($req->method) . '&' . uri_escape_utf8($base_url) . '&' . uri_escape_utf8($params_str); } 1; =head1 NAME WWW::OAuth - Portable OAuth 1.0 authentication =head1 SYNOPSIS use WWW::OAuth; my $oauth = WWW::OAuth->new( client_id => $client_id, client_secret => $client_secret, token => $token, token_secret => $token_secret, ); # Just retrieve authorization header my $auth_header = $oauth->authorization_header($http_request, { oauth_callback => $url }); $http_request->header(Authorization => $auth_header); # HTTP::Tiny use HTTP::Tiny; my $res = $oauth->authenticate(Basic => { method => 'GET', url => $url }) ->request_with(HTTP::Tiny->new); # HTTP::Request use HTTP::Request::Common; use LWP::UserAgent; my $res = $oauth->authenticate(GET $url)->request_with(LWP::UserAgent->new); # Mojo::Message::Request use Mojo::UserAgent; my $tx = $ua->build_tx(get => $url); $tx = $oauth->authenticate($tx->req)->request_with(Mojo::UserAgent->new); =head1 DESCRIPTION L implements OAuth 1.0 request authentication according to L (sometimes referred to as OAuth 1.0A). It does not implement the user agent requests needed for the complete OAuth 1.0 authorization flow; it only prepares and signs requests, leaving the rest up to your application. It can authenticate requests for L, L, L, and can be extended to operate on other types of requests. Some user agents can be configured to automatically authenticate each request with a L object. # LWP::UserAgent my $ua = LWP::UserAgent->new; $ua->add_handler(request_prepare => sub { $oauth->authenticate($_[0]) }); # Mojo::UserAgent my $ua = Mojo::UserAgent->new; $ua->on(start => sub { $oauth->authenticate($_[1]->req) }); =head1 RETRIEVING ACCESS TOKENS The process of retrieving access tokens and token secrets for authorization on behalf of a user may differ among various APIs, but it follows this general format (error checking is left as an exercise to the reader): use WWW::OAuth; use WWW::OAuth::Util 'form_urldecode'; use HTTP::Tiny; my $ua = HTTP::Tiny->new; my $oauth = WWW::OAuth->new( client_id => $client_id, client_secret => $client_secret, ); # Request token request my $res = $oauth->authenticate({ method => 'POST', url => $request_token_url }, { oauth_callback => $callback_url })->request_with($ua); my %res_data = @{form_urldecode $res->{content}}; my ($request_token, $request_secret) = @res_data{'oauth_token','oauth_token_secret'}; Now, the returned request token must be used to construct a URL for the user to go to and authorize your application. The exact method differs by API. The user will usually be redirected to the C<$callback_url> passed earlier after authorizing, with a verifier token that can be used to retrieve the access token and secret. # Access token request $oauth->token($request_token); $oauth->token_secret($request_secret); my $res = $oauth->authenticate({ method => 'POST', url => $access_token_url }, { oauth_verifier => $verifier_token })->request_with($ua); my %res_data = @{form_urldecode $res->{content}}; my ($access_token, $access_secret) = @res_data{'oauth_token','oauth_token_secret'}; Finally, the access token and secret can now be stored and used to authorize your application on behalf of this user. $oauth->token($access_token); $oauth->token_secret($access_secret); =head1 ATTRIBUTES L implements the following attributes. =head2 client_id my $client_id = $oauth->client_id; $oauth = $oauth->client_id($client_id); Client ID used to identify application (sometimes called an API key or consumer key). Required for all requests. =head2 client_secret my $client_secret = $oauth->client_secret; $oauth = $oauth->client_secret($client_secret); Client secret used to authenticate application (sometimes called an API secret or consumer secret). Required for all requests. =head2 token my $token = $oauth->token; $oauth = $oauth->token($token); Request or access token used to identify resource owner. Leave undefined for temporary credentials requests (request token requests). =head2 token_secret my $token_secret = $oauth->token_secret; $oauth = $oauth->token_secret($token_secret); Request or access token secret used to authenticate on behalf of resource owner. Leave undefined for temporary credentials requests (request token requests). =head2 signature_method my $method = $oauth->signature_method; $oauth = $oauth->signature_method($method); Signature method, can be C, C<HMAC-SHA1>, C<RSA-SHA1>, or a custom signature method. For C<RSA-SHA1> or custom signature methods, a L</"signer"> must be provided. Defaults to C<HMAC-SHA1>. =head2 signer my $signer = $oauth->signer; $oauth = $oauth->signer(sub { my ($base_str, $client_secret, $token_secret) = @_; ... return $signature; }); Coderef which implements the L</"signature_method">. A default signer is provided for signature methods C<PLAINTEXT> and C<HMAC-SHA1>; this attribute is required for other signature methods. For signature method C<RSA-SHA1>, this attribute may also be an object which has a C<sign> method like L<Crypt::OpenSSL::RSA>. The signer is passed the computed signature base string, the client secret, and (if present) the token secret, and must return the signature string. =head1 METHODS L<WWW::OAuth> implements the following methods. =head2 authenticate $container = $oauth->authenticate($container, \%oauth_params); my $container = $oauth->authenticate($http_request, \%oauth_params); my $container = $oauth->authenticate(Basic => { method => 'GET', url => $url }, \%oauth_params); Wraps the HTTP request in a container with L<WWW::OAuth::Util/"oauth_request">, then sets the Authorization header using L</"authorization_header"> to sign the request for OAuth 1.0. An optional hashref of OAuth parameters will be passed through to L</"authorization_header">. Returns the container object. =head2 authorization_header my $auth_header = $oauth->authorization_header($container, \%oauth_params); my $auth_header = $oauth->authorization_header($http_request, \%oauth_params); my $auth_header = $oauth->authorization_header(Basic => { method => 'GET', url => $url }, \%oauth_params); Forms an OAuth 1.0 signed Authorization header for the passed request. As in L</"authenticate">, the request may be specified in any form accepted by L<WWW::OAuth::Util/"oauth_request">. OAuth protocol parameters (starting with C<oauth_> or the special parameter C<realm>) may be optionally specified in a hashref and will override any generated protocol parameters of the same name (they should not be present in the request URL or body parameters). Returns the signed header value. =head1 HTTP REQUEST CONTAINERS Request containers provide a unified interface for L</"authenticate"> to parse and update HTTP requests. They must perform the L<Role::Tiny> role L<WWW::OAuth::Request>. Custom container classes can be instantiated directly or via L<WWW::OAuth::Util/"oauth_request">. =head2 Basic L<WWW::OAuth::Request::Basic> contains the request attributes directly, for user agents such as L<HTTP::Tiny> that do not use request objects. =head2 HTTP_Request L<WWW::OAuth::Request::HTTP_Request> wraps a L<HTTP::Request> object, which is compatible with several user agents including L<LWP::UserAgent>, L<HTTP::Thin>, and L<Net::Async::HTTP>. =head2 Mojo L<WWW::OAuth::Request::Mojo> wraps a L<Mojo::Message::Request> object, which is used by L<Mojo::UserAgent> via L<Mojo::Transaction>. =head1 BUGS Report any issues on the public bugtracker. This module was developed primarily to interface with the Twitter API, which is now functionally unusable, so further development of this module is unlikely without another use case. =head1 AUTHOR Dan Book <dbook@cpan.org> =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) =head1 SEE ALSO L<Net::OAuth>, L<Mojolicious::Plugin::OAuth2> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������request_mojo.t��������������������������������������������������������������������������������������100644��001750��001750�� 4771�14741612716� 16466� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/t������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use utf8; use Test::More; use Test::Needs { 'Mojolicious' => '6.0' }; use Mojo::Content::MultiPart; use Mojo::Message::Request; use Mojo::Parameters; use Mojo::URL; use WWW::OAuth::Util 'form_urlencode'; use WWW::OAuth::Request::Mojo; my $mojo_request = Mojo::Message::Request->new; $mojo_request->method('GET')->url->parse('http://example.com'); my $req = WWW::OAuth::Request::Mojo->new(request => $mojo_request); is $req->method, 'GET', 'method is GET'; $req->method('POST'); is $req->method, 'POST', 'method is POST'; is $req->url, 'http://example.com', 'url is set'; $req->url('https://example.com?foo=bar'); is $req->url, 'https://example.com?foo=bar', 'url is changed'; is $req->content, '', 'content is not set'; $req->content('foo=bar&baz=1'); is $req->content, 'foo=bar&baz=1', 'content is set'; is $req->header('FooBar'), undef, 'header FooBar is not set'; $req->header(FooBar => '☃'); is $req->header('FooBar'), '☃', 'header FooBar is set'; is $req->header('FOOBAR'), '☃', 'header FOOBAR is set'; is $req->header('foobar'), '☃', 'header foobar is set'; ok !$req->content_is_form, 'content is not a form'; $req->header('content-type' => 'application/x-www-form-urlencoded'); ok $req->content_is_form, 'content is a form'; $req->header('content-type' => 'application/not-a-form'); ok !$req->content_is_form, 'content is not a form'; $req->header('Content-Type' => 'application/x-www-form-urlencoded'); ok $req->content_is_form, 'content is a form'; $req->request->content(Mojo::Content::MultiPart->new); ok !$req->content_is_form, 'content is not a form'; is $req->header('MultiFoo'), undef, 'header MultiFoo is not set'; $req->header(MultiFoo => ['a', 'b', 'c']); is $req->header('MultiFoo'), 'a, b, c', 'header MultiFoo has multiple values'; $req->header(MultiFoo => 'abc'); is $req->header('MultiFoo'), 'abc', 'header MultiFoo has one value'; $req->url('http://example.com'); $req->content(''); is_deeply $req->query_pairs, [], 'no query parameters'; is_deeply $req->body_pairs, [], 'no body parameters'; $req->url('http://example.com?' . form_urlencode [foo => ['☃', '❤'], '❤' => 'a b c', baz => 0, bar => '☃']); is_deeply $req->query_pairs, ['foo', '☃', 'foo', '❤', '❤', 'a b c', 'baz', '0', 'bar', '☃'], 'URL has query parameters'; $req->content(form_urlencode [foo => ['☃', '❤'], '❤' => 'a b c', baz => 0, bar => '☃']); is_deeply $req->body_pairs, ['foo', '☃', 'foo', '❤', '❤', 'a b c', 'baz', '0', 'bar', '☃'], 'Request has body parameters'; done_testing; �������request_basic.t�������������������������������������������������������������������������������������100644��001750��001750�� 3543�14741612716� 16577� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/t������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use utf8; use Test::More; use WWW::OAuth::Util 'form_urlencode'; use WWW::OAuth::Request::Basic; my $req = WWW::OAuth::Request::Basic->new; is_deeply $req->headers, {}, 'no headers'; is $req->header('FooBar'), undef, 'header FooBar is not set'; $req->header(FooBar => '☃'); is $req->header('FooBar'), '☃', 'header FooBar is set'; is $req->header('FOOBAR'), '☃', 'header FOOBAR is set'; is $req->header('foobar'), '☃', 'header foobar is set'; ok !$req->content_is_form, 'content is not a form'; $req->header('content-type' => 'application/x-www-form-urlencoded'); ok $req->content_is_form, 'content is a form'; $req->header('content-type' => 'application/not-a-form'); ok !$req->content_is_form, 'content is not a form'; is $req->header('MultiFoo'), undef, 'header MultiFoo is not set'; $req->header(MultiFoo => ['a', 'b', 'c']); is $req->header('MultiFoo'), 'a, b, c', 'header MultiFoo has multiple values'; $req->header(MultiFoo => 'abc'); is $req->header('MultiFoo'), 'abc', 'header MultiFoo has one value'; $req->url('http://example.com'); $req->content(''); is_deeply $req->query_pairs, [], 'no query parameters'; is_deeply $req->body_pairs, [], 'no body parameters'; $req->url('http://example.com?' . form_urlencode [foo => ['☃', '❤'], '❤' => 'a b c', baz => 0, bar => '☃']); is_deeply $req->query_pairs, ['foo', '☃', 'foo', '❤', '❤', 'a b c', 'baz', '0', 'bar', '☃'], 'URL has query parameters'; $req->content(form_urlencode [foo => ['☃', '❤'], '❤' => 'a b c', baz => 0, bar => '☃']); is_deeply $req->body_pairs, ['foo', '☃', 'foo', '❤', '❤', 'a b c', 'baz', '0', 'bar', '☃'], 'Request has body parameters'; $req->header('Content-Type' => ''); $req->content(''); $req->set_form([foo => 'a b c']); is $req->content, 'foo=a+b+c', 'Set form content'; ok $req->content_is_form, 'content is a form'; done_testing; �������������������������������������������������������������������������������������������������������������������������������������������������������������00-report-prereqs.t���������������������������������������������������������������������������������100644��001750��001750�� 13636�14741612716� 17201� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/t������������������������������������������������������������������������������������������������������������������������������������������������������#!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.029 use Test::More tests => 1; use ExtUtils::MakeMaker; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ($collector, $prereqs) = @_; # CPAN::Meta::Prereqs object if (ref $collector eq $cpan_meta_pre) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new( $prereqs ) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( Mojolicious HTTP::Request ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do './t/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; my $cpan_meta_error; if ( $source && $HAS_CPAN_META && (my $meta = eval { CPAN::Meta->load_file($source) } ) ) { $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); } else { $cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source) $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase ( qw(configure build test runtime develop other) ) { next unless $req_hash->{$phase}; next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); for my $type ( qw(requires recommends suggests conflicts modules) ) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase).' '.ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if grep { $_ eq $mod } @exclude; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; if ($mod eq 'perl') { push @reports, ['perl', $want, $]]; next; } my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; if ($prefix) { my $have = MM->parse_version( File::Spec->catfile($prefix, $file) ); $have = "undef" unless defined $have; push @reports, [$mod, $want, $have]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [$mod, $want, "missing"]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if ( @reports ) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ($type eq 'modules') { splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; } else { splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; } push @full_reports, "\n"; } } } if ( @full_reports ) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( $cpan_meta_error || @dep_errors ) { diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n"; } if ( $cpan_meta_error ) { my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n"; } if ( @dep_errors ) { diag join("\n", "\nThe following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass('Reported prereqs'); # vim: ts=4 sts=4 sw=4 et: ��������������������������������������������������������������������������������������������������OAuth�����������������������������������������������������������������������������������������������000755��001750��001750�� 0�14741612716� 15423� 5����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/lib/WWW������������������������������������������������������������������������������������������������������������������������������������������������Util.pm���������������������������������������������������������������������������������������������100644��001750��001750�� 10572�14741612716� 17063� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/lib/WWW/OAuth������������������������������������������������������������������������������������������������������������������������������������������package WWW::OAuth::Util; use strict; use warnings; use Carp 'croak'; use Exporter 'import'; use Module::Runtime 'require_module'; use Role::Tiny (); use Scalar::Util 'blessed'; use WWW::Form::UrlEncoded 'build_urlencoded_utf8', 'parse_urlencoded_arrayref'; our $VERSION = '1.002'; our @EXPORT_OK = qw(form_urlencode form_urldecode oauth_request); sub form_urldecode { my $string = shift; return [] unless defined $string; my $form = parse_urlencoded_arrayref $string; utf8::decode $_ for @$form; return $form; } sub form_urlencode { my $form = shift; if (ref $form eq 'ARRAY') { croak 'Form to urlencode must be even-sized' if @$form % 2; } elsif (ref $form eq 'HASH') { $form = [map { ($_ => $form->{$_}) } sort keys %$form]; } else { croak 'Form to urlencode must be hash or array reference'; } return build_urlencoded_utf8 $form, '&'; } sub oauth_request { my $class = ref $_[0] ? undef : shift; my $proto = shift; my %args; if (blessed $proto) { # Request object return $proto if Role::Tiny::does_role($proto, 'WWW::OAuth::Request'); # already in container unless (defined $class) { if ($proto->isa('HTTP::Request')) { $class = 'HTTP_Request'; } elsif ($proto->isa('Mojo::Message::Request')) { $class = 'Mojo'; } else { $class = blessed $proto; $class =~ s/::/_/g; } } %args = (request => $proto); } elsif (ref $proto eq 'HASH') { # Hashref $class = 'Basic' unless defined $class; %args = %$proto; } else { croak 'No request or request parameters passed'; } $class = "WWW::OAuth::Request::$class" unless $class =~ /::/; require_module $class; croak "Class $class does not perform the role WWW::OAuth::Request" unless Role::Tiny::does_role($class, 'WWW::OAuth::Request'); return $class->new(%args); } 1; =head1 NAME WWW::OAuth::Util - Utility functions for WWW::OAuth =head1 SYNOPSIS use WWW::OAuth::Util 'form_urldecode', 'form_urlencode'; my $body_string = form_urlencode({foo => 'a b c', bar => [1, 2, 3]}); # bar=1&bar=2&bar=3&foo=a+b+c my $ordered_pairs = form_urldecode($body_string); # ['bar', '1', 'bar', '2', 'bar', '3', 'foo', 'a b c'] use WWW::OAuth::Util 'oauth_request'; my $container = oauth_request($http_request); =head1 DESCRIPTION L<WWW::OAuth::Util> contains utility functions for use with L<WWW::OAuth>. All functions are exportable on demand. =head1 FUNCTIONS =head2 form_urldecode my $param_pairs = form_urldecode($body_string); Decodes an C<application/x-www-form-urlencoded> string and returns an even-sized arrayref of key-value pairs. Order is preserved and repeated keys are not combined. =head2 form_urlencode my $body_string = form_urlencode([foo => 2, bar => 'baz', foo => 1]); # foo=2&bar=baz&foo=1 my $body_string = form_urlencode({foo => [2, 1], bar => 'baz'}); # bar=baz&foo=2&foo=1 Converts a hash or array reference into an C<application/x-www-form-urlencoded> string suitable for a query string or request body. If a value is an array reference, the key is repeated with each value. Order is preserved if parameters are passed in an array reference; the parameters are sorted by key for consistency if passed in a hash reference. =head2 oauth_request my $container = oauth_request($http_request); my $container = oauth_request({ method => 'GET', url => $url }); my $container = oauth_request(Basic => { method => 'POST', url => $url, content => $content }); Constructs an HTTP request container performing the L<WWW::OAuth::Request> role. The input should be a recognized request object or hashref of arguments optionally preceded by a container class name. The class name is appended to C<WWW::OAuth::Request::> if it does not contain C<::>. Currently, L<HTTP::Request> and L<Mojo::Message::Request> objects are recognized, and hashrefs are used to construct a L<WWW::OAuth::Request::Basic> object if no container class is specified. # Longer forms to construct WWW::OAuth::Request::HTTP_Request my $container = oauth_request(HTTP_Request => $http_request); my $container = oauth_request(HTTP_Request => { request => $http_request }); =head1 BUGS Report any issues on the public bugtracker. =head1 AUTHOR Dan Book <dbook@cpan.org> =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) =head1 SEE ALSO L<URI>, L<URL Living Standard|https://url.spec.whatwg.org/#application/x-www-form-urlencoded> ��������������������������������������������������������������������������������������������������������������������������������������author����������������������������������������������������������������������������������������������000755��001750��001750�� 0�14741612716� 15106� 5����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/xt�����������������������������������������������������������������������������������������������������������������������������������������������������pod-syntax.t����������������������������������������������������������������������������������������100644��001750��001750�� 252�14741612716� 17520� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/xt/author����������������������������������������������������������������������������������������������������������������������������������������������#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������00-report-prereqs.dd��������������������������������������������������������������������������������100644��001750��001750�� 5012�14741612716� 17272� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/t������������������������������������������������������������������������������������������������������������������������������������������������������do { my $x = { 'configure' => { 'requires' => { 'ExtUtils::MakeMaker' => '0' } }, 'develop' => { 'recommends' => { 'Mojolicious' => '7.54' }, 'requires' => { 'HTTP::Request' => '0', 'HTTP::Tiny' => '0.014', 'LWP::UserAgent' => '0', 'Pod::Coverage::TrustPod' => '0', 'Test::Pod' => '1.41', 'Test::Pod::Coverage' => '1.08', 'Test::TCP' => '0' } }, 'runtime' => { 'recommends' => { 'WWW::Form::UrlEncoded::XS' => '0.23' }, 'requires' => { 'Carp' => '0', 'Class::Tiny::Chained' => '0', 'Crypt::URandom' => '0.37', 'Digest::SHA' => '0', 'List::Util' => '1.33', 'Module::Runtime' => '0', 'Role::Tiny' => '2.000000', 'Scalar::Util' => '0', 'URI' => '1.28', 'URI::Escape' => '3.26', 'WWW::Form::UrlEncoded' => '0.23', 'perl' => '5.008001' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900' }, 'requires' => { 'Data::Dumper' => '0', 'ExtUtils::MakeMaker' => '0', 'File::Spec' => '0', 'JSON::PP' => '0', 'Test::More' => '0.88', 'Test::Needs' => '0' } } }; $x; }����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������request_ua.t����������������������������������������������������������������������������������������100644��001750��001750�� 4652�14741612716� 17617� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/xt/author����������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use if $^O eq 'MSWin32', 'Test::More' => skip_all => 'Forking may be problematic on Windows'; BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } use Test::More; use Test::Needs { Mojolicious => '7.54' }; use Test::TCP; use HTTP::Tiny; use HTTP::Request; use LWP::UserAgent; use Mojolicious::Lite; use Mojo::IOLoop; use Mojo::Server::Daemon; use Mojo::UserAgent; use Module::Runtime 'use_module'; use WWW::OAuth::Util 'oauth_request'; my $server = Test::TCP->new(code => sub { my $port = shift; my $guard = Mojo::IOLoop->timer(5 => sub { Mojo::IOLoop->stop }); app->log->level('warn'); get '/' => sub { my $c = shift; $c->render(text => 'foo'); }; my $daemon = Mojo::Server::Daemon->new(listen => ["http://127.0.0.1:$port"], app => app); $daemon->run; exit; }); my $port = $server->port; my $req = oauth_request({method => 'GET', url => "http://127.0.0.1:$port"}); my $res = $req->request_with(HTTP::Tiny->new); ok $res->{success}, 'request succeeded'; is $res->{content}, 'foo', 'got response'; my $http_req = oauth_request(HTTP::Request->new(GET => "http://127.0.0.1:$port")); my $http_res = $http_req->request_with(LWP::UserAgent->new); ok $http_res->is_success, 'request succeeded'; is $http_res->content, 'foo', 'got response'; my $ua = Mojo::UserAgent->new; my $tx = $ua->build_tx(GET => "http://127.0.0.1:$port"); my $mojo_req = oauth_request($tx->req); $tx = $mojo_req->request_with($ua); ok !$tx->error, 'request succeeded'; is $tx->res->body, 'foo', 'got response'; $tx = $ua->build_tx(GET => "http://127.0.0.1:$port"); $mojo_req = oauth_request($tx->req); my $got_response; $mojo_req->request_with($ua, sub { my ($ua, $tx) = @_; ok !$tx->error, 'request succeeded'; is $tx->res->body, 'foo', 'got response'; $got_response = 1; Mojo::IOLoop->stop; }); my $timeout = Mojo::IOLoop->timer(1 => sub { Mojo::IOLoop->stop }); Mojo::IOLoop->start; Mojo::IOLoop->remove($timeout); ok $got_response, 'response was handled'; $tx = $ua->build_tx(GET => "http://127.0.0.1:$port"); $mojo_req = oauth_request($tx->req); undef $got_response; $mojo_req->request_with_p($ua)->then(sub { my $tx = shift; ok !$tx->error, 'request succeeded'; is $tx->res->body, 'foo', 'got response'; $got_response = 1; Mojo::IOLoop->stop; }); $timeout = Mojo::IOLoop->timer(1 => sub { Mojo::IOLoop->stop }); Mojo::IOLoop->start; Mojo::IOLoop->remove($timeout); ok $got_response, 'response was handled'; undef $server; done_testing; ��������������������������������������������������������������������������������������pod-coverage.t��������������������������������������������������������������������������������������100644��001750��001750�� 365�14741612716� 17772� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/xt/author����������������������������������������������������������������������������������������������������������������������������������������������#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests. use strict; use warnings; use Test::Pod::Coverage 1.08; use Pod::Coverage::TrustPod; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Request.pm������������������������������������������������������������������������������������������100644��001750��001750�� 5027�14741612716� 17555� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/lib/WWW/OAuth������������������������������������������������������������������������������������������������������������������������������������������package WWW::OAuth::Request; use URI; use WWW::OAuth::Util 'form_urldecode'; use Role::Tiny; our $VERSION = '1.002'; requires 'method', 'url', 'content', 'content_is_form', 'header', 'request_with'; sub query_pairs { [map { utf8::decode $_; $_ } URI->new(shift->url)->query_form] } sub body_pairs { form_urldecode shift->content } 1; =head1 NAME WWW::OAuth::Request - HTTP Request container role =head1 SYNOPSIS use Role::Tiny::With; with 'WWW::OAuth::Request'; =head1 DESCRIPTION L<WWW::OAuth::Request> is a L<Role::Tiny> role that provides a consistent interface to L<WWW::OAuth> for parsing and authenticating requests. See L<WWW::OAuth/"HTTP REQUEST CONTAINERS"> for specifics. =head1 METHODS L<WWW::OAuth::Request> implements or requires the following methods. =head2 body_pairs my $pairs = $req->body_pairs; Return body parameters from C<application/x-www-form-urlencoded> L</"content"> as an even-sized arrayref of keys and values. =head2 content my $content = $req->content; $req = $req->content('foo=1&baz=2'); Set or return request content. Must be implemented to compose role. =head2 content_is_form my $bool = $req->content_is_form; Check whether content is single-part and content type is C<application/x-www-form-urlencoded>. Must be implemented to compose role. =head2 header my $header = $req->header('Content-Type'); $req = $req->header('Content-Type' => 'application/x-www-form-urlencoded'); Set or return a request header. Multiple values can be set by passing an array reference as the value, and multi-value headers are joined on C<, > when returned. Must be implemented to compose role. =head2 method my $method = $req->method; $req = $req->method('GET'); Set or return request method. Must be implemented to compose role. =head2 query_pairs my $pairs = $req->query_pairs; Return query parameters from L</"url"> as an even-sized arrayref of keys and values. =head2 request_with my $res = $req->request_with($ua); Send request using passed user-agent object, and return response. Must be implemented to compose role. =head2 url my $url = $req->url; $req = $req->url('http://example.com/api/'); Set or return request URL. Must be implemented to compose role. =head1 BUGS Report any issues on the public bugtracker. =head1 AUTHOR Dan Book <dbook@cpan.org> =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) =head1 SEE ALSO L<HTTP::Request>, L<Mojo::Message::Request> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������request_http_request.t������������������������������������������������������������������������������100644��001750��001750�� 4570�14741612716� 20246� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/t������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; use utf8; use Test::More; use Test::Needs 'HTTP::Request'; use WWW::OAuth::Util 'form_urlencode'; use WWW::OAuth::Request::HTTP_Request; my $http_request = HTTP::Request->new(GET => 'http://example.com'); my $req = WWW::OAuth::Request::HTTP_Request->new(request => $http_request); is $req->method, 'GET', 'method is GET'; $req->method('POST'); is $req->method, 'POST', 'method is POST'; is $req->url, 'http://example.com', 'url is set'; $req->url('https://example.com?foo=bar'); is $req->url, 'https://example.com?foo=bar', 'url is changed'; is $req->content, '', 'content is not set'; $req->content('foo=bar&baz=1'); is $req->content, 'foo=bar&baz=1', 'content is set'; is $req->header('FooBar'), undef, 'header FooBar is not set'; $req->header(FooBar => '☃'); is $req->header('FooBar'), '☃', 'header FooBar is set'; is $req->header('FOOBAR'), '☃', 'header FOOBAR is set'; is $req->header('foobar'), '☃', 'header foobar is set'; ok !$req->content_is_form, 'content is not a form'; $req->header('content-type' => 'application/x-www-form-urlencoded'); ok $req->content_is_form, 'content is a form'; $req->header('content-type' => 'application/not-a-form'); ok !$req->content_is_form, 'content is not a form'; $req->header('Content-Type' => 'application/x-www-form-urlencoded'); ok $req->content_is_form, 'content is a form'; $req->request->parts(HTTP::Message->new, HTTP::Message->new); ok !$req->content_is_form, 'content is not a form'; is $req->header('MultiFoo'), undef, 'header MultiFoo is not set'; $req->header(MultiFoo => ['a', 'b', 'c']); is $req->header('MultiFoo'), 'a, b, c', 'header MultiFoo has multiple values'; $req->header(MultiFoo => 'abc'); is $req->header('MultiFoo'), 'abc', 'header MultiFoo has one value'; $req->url('http://example.com'); $req->content(''); is_deeply $req->query_pairs, [], 'no query parameters'; is_deeply $req->body_pairs, [], 'no body parameters'; $req->url('http://example.com?' . form_urlencode [foo => ['☃', '❤'], '❤' => 'a b c', baz => 0, bar => '☃']); is_deeply $req->query_pairs, ['foo', '☃', 'foo', '❤', '❤', 'a b c', 'baz', '0', 'bar', '☃'], 'URL has query parameters'; $req->content(form_urlencode [foo => ['☃', '❤'], '❤' => 'a b c', baz => 0, bar => '☃']); is_deeply $req->body_pairs, ['foo', '☃', 'foo', '❤', '❤', 'a b c', 'baz', '0', 'bar', '☃'], 'Request has body parameters'; done_testing; ����������������������������������������������������������������������������������������������������������������������������������������Request���������������������������������������������������������������������������������������������000755��001750��001750�� 0�14741612716� 17053� 5����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/lib/WWW/OAuth������������������������������������������������������������������������������������������������������������������������������������������Mojo.pm���������������������������������������������������������������������������������������������100644��001750��001750�� 11472�14741612716� 20502� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/lib/WWW/OAuth/Request����������������������������������������������������������������������������������������������������������������������������������package WWW::OAuth::Request::Mojo; use strict; use warnings; use Class::Tiny::Chained 'request'; use Carp 'croak'; use Scalar::Util 'blessed'; use Role::Tiny::With; with 'WWW::OAuth::Request'; our $VERSION = '1.002'; sub method { my $self = shift; return $self->request->method unless @_; $self->request->method(shift); return $self; } sub url { my $self = shift; return $self->request->url->to_string unless @_; require Mojo::URL; $self->request->url(Mojo::URL->new(shift)); return $self; } sub content { my $self = shift; return $self->request->body unless @_; $self->request->body(shift); return $self; } sub content_is_form { my $self = shift; return 0 if $self->request->content->is_multipart; my $content_type = $self->request->headers->content_type; return 0 unless defined $content_type and $content_type =~ m!application/x-www-form-urlencoded!i; return 1; } sub query_pairs { shift->request->query_params->pairs } sub body_pairs { require Mojo::Parameters; Mojo::Parameters->new(shift->request->body)->pairs } sub header { my $self = shift; my $name = shift; croak 'No header to set/retrieve' unless defined $name; return $self->request->headers->header($name) unless @_; my @values = ref $_[0] eq 'ARRAY' ? @{$_[0]} : $_[0]; $self->request->headers->header($name => @values); return $self; } sub request_with { my ($self, $ua, $cb) = @_; croak 'Unknown user-agent object' unless blessed $ua and $ua->isa('Mojo::UserAgent'); return $ua->start($self->_build_tx($ua), $cb); } sub request_with_p { my ($self, $ua) = @_; croak 'Unknown user-agent object' unless blessed $ua and $ua->isa('Mojo::UserAgent'); my $has_promises = do { local $@; eval { require Mojolicious; Mojolicious->VERSION('7.54'); 1 } }; croak 'Mojolicious 7.54 required for request_with_p' unless $has_promises; return $ua->start_p($self->_build_tx($ua)); } sub _build_tx { my ($self, $ua) = @_; return $ua->build_tx($self->method, $self->url, $self->request->headers->to_hash, $self->content); } 1; =head1 NAME WWW::OAuth::Request::Mojo - HTTP Request container for Mojo::Message::Request =head1 SYNOPSIS my $req = WWW::OAuth::Request::Mojo->new(request => $mojo_request); my $ua = Mojo::UserAgent->new; my $tx = $req->request_with($ua); $req->request_with_p($ua)->then(sub { my $tx = shift; }); =head1 DESCRIPTION L<WWW::OAuth::Request::Mojo> is a request container for L<WWW::OAuth> that wraps a L<Mojo::Message::Request> object, which is used by L<Mojo::UserAgent>. It performs the role L<WWW::OAuth::Request>. =head1 ATTRIBUTES L<WWW::OAuth::Request::Mojo> implements the following attributes. =head2 request my $mojo_request = $req->request; $req = $req->request($mojo_request); L<Mojo::Message::Request> object to authenticate. =head1 METHODS L<WWW::OAuth::Request::Mojo> composes all methods from L<WWW::OAuth::Request>, and implements the following new ones. =head2 body_pairs my $pairs = $req->body_pairs; Return body parameters from L</"request"> as an even-sized arrayref of keys and values. =head2 content my $content = $req->content; $req = $req->content('foo=1&bar=2'); Set or return request content from L</"request">. =head2 content_is_form my $bool = $req->content_is_form; Check whether L</"request"> has single-part content and a C<Content-Type> header of C<application/x-www-form-urlencoded>. =head2 header my $header = $req->header('Content-Type'); $req = $req->header(Authorization => 'foo bar'); Set or return a request header from L</"request">. =head2 method my $method = $req->method; $req = $req->method('GET'); Set or return request method from L</"request">. =head2 query_pairs my $pairs = $req->query_pairs; Return query parameters from L</"request"> as an even-sized arrayref of keys and values. =head2 request_with my $tx = $req->request_with($ua); $req->request_with($ua, sub { my ($ua, $tx) = @_; ... }); Run request with passed L<Mojo::UserAgent> user-agent object, and return L<Mojo::Transaction> object, as in L<Mojo::UserAgent/"start">. A callback can be passed to perform the request non-blocking. =head2 request_with_p my $p = $req->request_with_p($ua)->then(sub { my $tx = shift; ... }); Run non-blocking request with passed L<Mojo::UserAgent> user-agent object, and return a L<Mojo::Promise> which will be resolved with the successful transaction or rejected on a connection error, as in L<Mojo::UserAgent/"start_p">. =head2 url my $url = $req->url; $req = $req->url('http://example.com/api/'); Set or return request URL from L</"request">. =head1 BUGS Report any issues on the public bugtracker. =head1 AUTHOR Dan Book <dbook@cpan.org> =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) =head1 SEE ALSO L<Mojo::UserAgent> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Basic.pm��������������������������������������������������������������������������������������������100644��001750��001750�� 7522�14741612716� 20600� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/lib/WWW/OAuth/Request����������������������������������������������������������������������������������������������������������������������������������package WWW::OAuth::Request::Basic; use strict; use warnings; use Class::Tiny::Chained 'method', 'url', 'content', { headers => sub { {} } }; use Carp 'croak'; use List::Util 'first'; use Scalar::Util 'blessed'; use WWW::OAuth::Util 'form_urlencode'; use Role::Tiny::With; with 'WWW::OAuth::Request'; our $VERSION = '1.002'; sub content_is_form { my $self = shift; my $content_type = $self->header('Content-Type'); return 0 unless defined $content_type and $content_type =~ m!application/x-www-form-urlencoded!i; return 1; } sub header { my $self = shift; my $name = shift; croak 'No header to set/retrieve' unless defined $name; my $headers = $self->headers; unless (@_) { # workaround for TEMP bug in first/lc my @names = keys %$headers; my $key = first { lc $_ eq lc $name } @names; return undef unless defined $key; my @values = ref $headers->{$key} eq 'ARRAY' ? @{$headers->{$key}} : $headers->{$key}; return join ', ', grep { defined } @values; } my $value = shift; my @existing = grep { lc $_ eq lc $name } keys %$headers; delete @$headers{@existing} if @existing; $headers->{$name} = $value; return $self; } sub set_form { my ($self, $form) = @_; $self->header('Content-Type' => 'application/x-www-form-urlencoded'); $self->content(form_urlencode $form); return $self; } sub request_with { my ($self, $ua) = @_; croak 'Unknown user-agent object' unless blessed $ua and $ua->isa('HTTP::Tiny'); return $ua->request($self->method, $self->url, { headers => $self->headers, content => $self->content }); } 1; =head1 NAME WWW::OAuth::Request::Basic - HTTP Request container for HTTP::Tiny =head1 SYNOPSIS my $req = WWW::OAuth::Request::Basic->new(method => 'POST', url => $url, content => $content); $req->request_with(HTTP::Tiny->new); =head1 DESCRIPTION L<WWW::OAuth::Request::Basic> is a request container for L<WWW::OAuth> that stores the request parameters directly, for use with user-agents that do not use request objects like L<HTTP::Tiny>. It performs the role L<WWW::OAuth::Request>. =head1 ATTRIBUTES L<WWW::OAuth::Request::Basic> implements the following attributes. =head2 content my $content = $req->content; $req = $req->content('foo=1&bar=2'); Request content string. =head2 headers my $headers = $req->headers; $req = $req->headers({}); Hashref of request headers. Must be updated carefully as headers are case-insensitive. Values can be array references to specify multi-value headers. =head2 method my $method = $req->method; $req = $req->method('GET'); Request method. =head2 url my $url = $req->url; $req = $req->url('http://example.com/api/'); Request URL. =head1 METHODS L<WWW::OAuth::Request::Basic> composes all methods from L<WWW::OAuth::Request>, and implements the following new ones. =head2 content_is_form my $bool = $req->content_is_form; Check whether L</"headers"> contains a C<Content-Type> header set to C<application/x-www-form-urlencoded>. =head2 header my $header = $req->header('Content-Type'); $req = $req->header(Authorization => 'Basic foobar'); Set or return a request header in L</"headers">. =head2 set_form $req = $req->set_form({foo => 'bar'}); Convenience method to set L</"content"> to a urlencoded form. Equivalent to: use WWW::OAuth::Util 'form_urlencode'; $req->header('Content-Type' => 'application/x-www-form-urlencoded'); $req->content(form_urlencode $form); =head2 request_with my $res = $req->request_with(HTTP::Tiny->new); Run request with passed L<HTTP::Tiny> user-agent object, and return response hashref, as in L<HTTP::Tiny/"request">. =head1 BUGS Report any issues on the public bugtracker. =head1 AUTHOR Dan Book <dbook@cpan.org> =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) =head1 SEE ALSO L<HTTP::Tiny> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������HTTP_Request.pm�������������������������������������������������������������������������������������100644��001750��001750�� 7546�14741612716� 22054� 0����������������������������������������������������������������������������������������������������ustar�00grinnz��������������������������grinnz��������������������������000000��000000��WWW-OAuth-1.002/lib/WWW/OAuth/Request����������������������������������������������������������������������������������������������������������������������������������package WWW::OAuth::Request::HTTP_Request; use strict; use warnings; use Class::Tiny::Chained 'request'; use Carp 'croak'; use Scalar::Util 'blessed'; use Role::Tiny::With; with 'WWW::OAuth::Request'; our $VERSION = '1.002'; sub method { my $self = shift; return $self->request->method unless @_; $self->request->method(shift); return $self; } sub url { my $self = shift; return $self->request->uri->as_string unless @_; $self->request->uri(shift); return $self; } sub content { my $self = shift; return $self->request->content unless @_; $self->request->content(shift); return $self; } sub content_is_form { my $self = shift; my @parts = $self->request->parts; return 0 if @parts; my $content_type = $self->request->headers->content_type; return 0 unless defined $content_type and $content_type =~ m!application/x-www-form-urlencoded!i; return 1; } sub header { my $self = shift; my $name = shift; croak 'No header to set/retrieve' unless defined $name; return scalar $self->request->header($name) unless @_; $self->request->header($name => shift); return $self; } sub request_with { my ($self, $ua) = @_; croak 'Invalid user-agent object' unless blessed $ua; if ($ua->isa('LWP::UserAgent') or $ua->isa('HTTP::Thin')) { return $ua->request($self->request); } elsif ($ua->isa('Net::Async::HTTP')) { return $ua->do_request(request => $self->request); } else { my $class = blessed $ua; croak "Unknown user-agent class $class"; } } 1; =head1 NAME WWW::OAuth::Request::HTTP_Request - HTTP Request container for HTTP::Request =head1 SYNOPSIS my $req = WWW::OAuth::Request::HTTP_Request->new(request => $http_request); $req->request_with(LWP::UserAgent->new); =head1 DESCRIPTION L<WWW::OAuth::Request::HTTP_Request> is a request container for L<WWW::OAuth> that wraps a L<HTTP::Request> object, which can be used by several user-agents like L<LWP::UserAgent>, L<HTTP::Thin>, and L<Net::Async::HTTP>. It performs the role L<WWW::OAuth::Request>. =head1 ATTRIBUTES L<WWW::OAuth::Request::HTTP_Request> implements the following attributes. =head2 request my $http_request = $req->request; $req = $req->request(HTTP::Request->new(GET => $url)); L<HTTP::Request> object to authenticate. =head1 METHODS L<WWW::OAuth::Request::HTTP_Request> composes all methods from L<WWW::OAuth::Request>, and implements the following new ones. =head2 content my $content = $req->content; $req = $req->content('foo=1&bar=2'); Set or return request content from L</"request">. =head2 content_is_form my $bool = $req->content_is_form; Check whether L</"request"> has single-part content and a C<Content-Type> header of C<application/x-www-form-urlencoded>. =head2 header my $header = $req->header('Content-Type'); $req = $req->header(Authorization => 'Basic foobar'); Set or return a request header from L</"request">. =head2 method my $method = $req->method; $req = $req->method('GET'); Set or return request method from L</"request">. =head2 request_with $http_response = $req->request_with(LWP::UserAgent->new); Run request with passed user-agent object, and return L<HTTP::Response> object. User-agent may be L<LWP::UserAgent>, L<HTTP::Thin>, or L<Net::Async::HTTP>. If run with L<Net::Async::HTTP>, the return value is a L<Future> yielding the L<HTTP::Response> object as in L<< "do_request" in Net::Async::HTTP|Net::Async::HTTP/"$response = $http->do_request( %args )->get" >>. =head2 url my $url = $req->url; $req = $req->url('http://example.com/api/'); Set or return request URL from L</"request">. =head1 BUGS Report any issues on the public bugtracker. =head1 AUTHOR Dan Book <dbook@cpan.org> =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) =head1 SEE ALSO L<LWP::UserAgent>, L<HTTP::Thin>, L<Net::Async::HTTP> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������