| OAuth-Lite documentation | Contained in the OAuth-Lite distribution. |
OAuth::Lite::Consumer - consumer agent
my $consumer = OAuth::Lite::Consumer->new(
consumer_key => $consumer_key,
consumer_secret => $consumer_secret,
site => q{http://api.example.org},
request_token_path => q{/request_token},
access_token_path => q{/access_token},
authorize_path => q{http://example.org/authorize},
);
# At first you have to publish request-token, and
# with it, redirect end-user to authorization-url that Service Provider tell you beforehand.
my $request_token = $consumer->get_request_token(
callback_url => q{http://yourservice/callback},
);
$your_app->session->set( request_token => $request_token );
$your_app->redirect( $consumer->url_to_authorize(
token => $request_token,
) );
# After user authorize the request on a Service Provider side web application.
my $verifier = $your_app->request->param('oauth_verifier');
my $request_token = $your_app->session->get('request_token');
my $access_token = $consumer->get_access_token(
token => $request_token,
verifier => $verifier,
);
$your_app->session->set( access_token => $access_token );
$your_app->session->remove('request_token');
# After all, you can request protected-resources with access token
my $access_token = $your_app->session->get('access_token');
my $res = $consumer->request(
method => 'GET',
url => q{http://api.example.org/picture},
token => $access_token,
params => { file => 'mypic.jpg', size => 'small' },
);
unless ($res->is_success) {
if ($res->status == 400 || $res->status == 401) {
my $auth_header = $res->header('WWW-Authenticate');
if ($auth_header && $auth_header =~ /^OAuth/) {
# access token may be expired,
# get request-token and authorize again
} else {
# another auth error.
}
}
# another error.
}
# OAuth::Lite::Agent automatically adds Accept-Encoding gzip header to
# request, so, when you use default agent, call decoded_content.
my $resource = $res->decoded_content || $res->content;
$your_app->handle_resource($resource);
This module helps you to build OAuth Consumer application.
consumer_key value
consumer_secret value
Signature method you can choose from 'HMAC-SHA1', 'PLAINTEXT', and 'RSA-SHA1' (optional, 'HMAC-SHA1' is set by default)
HTTP method (GET or POST) when the request is for request token or access token. (optional, 'POST' is set by default)
OAuth::Lite::AuthMethod's value you can choose from AUTH_HEADER, POST_BODY and URL_QUERY (optional, AUTH_HEADER is set by default)
The OAuth realm value for a protected-resource you wanto to access to. (optional. empty-string is set by default)
If you use Request Body Hash extension, set 1. See Also http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/4/spec.html
The base site url of Service Provider
Site and other paths, simple usage.
my $consumer = OAuth::Lite::Consumer->new(
...
site => q{http://example.org},
request_token_path => q{/request_token},
access_token_path => q{/access_token},
authorize_path => q{/authorize},
);
say $consumer->request_token_url; # http://example.org/request_token
say $consumer->access_token_url; # http://example.org/access_token
say $consumer->authorization_url; # http://example.org/authorize
If the authorization_url is run under another domain, for example.
my $consumer = OAuth::Lite::Consumer->new(
...
site => q{http://api.example.org},
request_token_path => q{/request_token},
access_token_path => q{/access_token},
authorize_path => q{http://www.example.org/authorize},
);
say $consumer->request_token_url; # http://api.example.org/request_token
say $consumer->access_token_url; # http://api.example.org/access_token
say $consumer->authorization_url; # http://www.example.org/authorize
Like this, if you pass absolute url, consumer uses them as it is.
You can omit site param, if you pass all paths as absolute url.
my $consumer = OAuth::Lite::Consumer->new(
...
request_token_path => q{http://api.example.org/request_token},
access_token_path => q{http://api.example.org/access_token},
authorize_path => q{http://www.example.org/authorize},
);
And there is a flexible way.
# don't set each paths here.
my $consumer = OAuth::Lite::Consumer->new(
consumer_key => $consumer_key,
consumer_secret => $consumer_secret,
);
# set request token url here directly
my $rtoken = $consumer->get_request_token(
url => q{http://api.example.org/request_token},
callback_url => q{http://www.yourservice/callback},
);
# set authorize path here directly
my $url = $consumer->url_to_authorize(
token => $rtoken,
url => q{http://www.example.org/authorize},
);
# set access token url here directly
my $atoken = $consumer->get_access_token(
url => q{http://api.example.org/access_token},
verifier => $verfication_code,
);
So does callback_url. You can set it on consutructor or get_request_token method directly.
my $consumer = OAuth::Lite::Consumer->new(
...
callback_url => q{http://www.yourservice/callback},
);
...
my $url = $consumer->get_request_token();
Or
my $consumer = OAuth::Lite::Consumer->new(
...
);
...
my $url = $consumer->get_request_token(
callback_url => q{http://www.yourservice/callback},
);
authorization url, you can omit this if you set authorization_path on constructor.
request token value
my $url = $consumer->url_to_authorize(
url => q{http://example.org/authorize},
token => $request_token,
callback_url => q{http://www.yousrservice/callback},
);
Returns a request token as an OAuth::Lite::Response object. Except for that, this method behaves same as get_request_token.
Returns a request token as an OAuth::Lite::Token object.
Request token url. You can omit this if you set request_token_path on constructor
Realm for the resource you want to access to. You can omit this if you set realm on constructor.
Url which service provider redirect end-user to after authorization. You can omit this if you set callback_url on constructor.
my $token = $consumer->get_request_token(
url => q{http://api.example.org/request_token},
realm => q{http://api.example.org/picture},
) or die $consumer->errstr;
say $token->token;
say $token->secret;
my $res = $consumer->obtain_access_token(
url => $access_token_url,
params => {
x_auth_username => "myname",
x_auth_password => "mypass",
x_auth_mode => "client_auth",
},
);
my $access_token = $res->token;
say $acces_token->token;
say $acces_token->secret;
my $expires = $res->param('x_auth_expires');
What is the difference between obtain_access_token and get_access_token? get_access_token requires token and verifier. obtain_access_token doesn't. these parameters are optional. You can pass extra parameters like above example.(see x_auth_XXX parameters) And get_access_token returns OAuth::Lite::Token object directly, obtain_access_token returns OAuth::Lite::Response object that includes not only Token object but also other response parameters. the extra parameters are used for some extensions.(Session extension, xAuth, etc.)
Of cource, if you don't need to handle these extensions, You can continue to use get_access_token for backward compatibility.
my $token = $consumer->get_access_token(
url => $access_token_url,
token => $request_token,
verifier => $verification_code,
);
# above code's behavior is same as (but response objects are different)
my $res = $consumer->obtain_access_token(
url => $access_token_url,
token => $request_token,
params => {
oauth_verifier => $verification_code,
},
);
Returns a access token as an OAuth::Lite::Token object.
Request token url. You can omit this if you set request_token_path on constructor
Realm for the resource you want to access to. You can omit this if you set realm on constructor.
Request token object.
Verfication code which provider returns.
my $token = $consumer->get_access_token(
url => q{http://api.example.org/request_token},
realm => q{http://api.example.org/picture},
token => $request_token,
verifier => $verification_code,
) or die $consumer->errstr;
say $token->token;
say $token->secret;
Returns HTTP::Request object.
my $req = $consumer->gen_oauth_request(
method => 'GET',
url => 'http://example.com/',
headers => [ Accept => q{...}, 'Content-Type' => q{...}, ... ],
content => $content,
realm => $realm,
token => $token,
params => { file => 'mypic.jpg', size => 'small' },
);
Returns HTTP::Response object.
Realm for a resource you want to access
Access token OAuth::Lite::Token object
HTTP method.
Request URL
Extra params.
body data sent when method is POST or PUT.
my $response = $consumer->request(
method => 'POST',
url => 'http://api.example.com/picture',
headers => [ Accept => q{...}, 'Content-Type' => q{...}, ... ],
content => $content,
realm => $realm,
token => $access_token,
params => { file => 'mypic.jpg', size => 'small' },
);
unless ($response->is_success) {
...
}
There are simple methods to request protected resources. You need to obtain access token and set it to consumer beforehand.
my $access_token = $consumer->get_access_token(
token => $request_token,
verifier => $verifier,
);
# when successfully got an access-token,
# it internally execute saving method like following line.
# $consumer->access_token( $access_token )
or my $access_token = $your_app->pick_up_saved_access_token(); $consumer->access_token($access_token);
Then you can access protected resource in a simple way.
my $res = $consumer->get( 'http://api.example.com/pictures' );
if ($res->is_success) {
say $res->decoded_content||$res->content;
}
This is same as
my $res = $consumer->request(
method => q{GET},
url => q{http://api.example.com/picture},
);
if ($res->is_success) {
say $res->decoded_content||$res->content;
}
$res = $consumer->post( 'http://api.example.com/pictures', $content );
if ($res->is_success) {
...
}
This is same as
$res = $consumer->request(
method => q{POST},
url => q{http://api.example.com/picture},
content => $content,
);
if ($res->is_success) {
...
}
$res = $consumer->put( 'http://api.example.com/pictures', $content );
if ($res->is_success) {
...
}
This is same as
my $res = $consumer->request(
method => q{PUT},
url => q{http://api.example.com/picture},
content => $content,
);
if ($res->is_success) {
...
}
my $res = $consumer->delete('http://api.example.com/delete');
if ($res->is_success) {
...
}
This is same as
my $res = $consumer->request(
method => q{DELETE},
url => q{http://api.example.com/picture},
);
if ($res->is_success) {
...
}
realm for a resource you want to access
OAuth::Lite::Token object(optional)
my $header = $consumer->gen_auth_header($method, $url, {
realm => $realm,
token => $token,
});
Generates and returns all oauth params.
my $params = $consumer->gen_auth_params($http_method, $request_url);
say $params->{oauth_consumer_key};
say $params->{oauth_timestamp};
say $params->{oauth_nonce};
say $params->{oauth_signature_method};
say $params->{oauth_signature};
say $params->{oauth_version};
If you pass token as third argument, the result includes oauth_token value.
my $params = $consumer->gen_auth_params($http_method, $request_url, $token);
say $params->{oauth_consumer_key};
say $params->{oauth_timestamp};
say $params->{oauth_nonce};
say $params->{oauth_signature_method};
say $params->{oauth_signature};
say $params->{oauth_token};
say $params->{oauth_version};
Returns last oauth request.
my $req_token = $consumer->get_request_token(...);
say $consumer->oauth_request->uri;
my $req_token = $consumer->get_access_token(...);
say $consumer->oauth_request->uri;
Returns last oauth response.
my $req_token = $consumer->get_request_token(...);
say $consumer->oauth_response->status;
my $req_token = $consumer->get_access_token(...);
say $consumer->oauth_response->status;
remove last oauth-request and oauth-response.
Build body hash according to the spec for 'OAuth Request Body Hash extension' http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/4/spec.html
my $hash = $self->build_body_hash($content);
Lyo Kato, lyo.kato _at_ gmail.com
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available.
| OAuth-Lite documentation | Contained in the OAuth-Lite distribution. |
package OAuth::Lite::Consumer; use strict; use warnings; use base qw( Class::ErrorHandler Class::Accessor::Fast ); __PACKAGE__->mk_accessors(qw( consumer_key consumer_secret oauth_request oauth_response request_token access_token )); *oauth_req = \&oauth_request; *oauth_res = \&oauth_response; use Carp (); use bytes (); use URI; use HTTP::Request; use HTTP::Headers; use UNIVERSAL::require; use List::MoreUtils qw(any); use OAuth::Lite; use OAuth::Lite::Agent; use OAuth::Lite::Token; use OAuth::Lite::Response; use OAuth::Lite::Util qw(:all); use OAuth::Lite::AuthMethod qw(:all);
sub new { my ($class, %args) = @_; my $ua = delete $args{ua}; unless ($ua) { $ua = OAuth::Lite::Agent->new; $ua->agent(join "/", __PACKAGE__, $OAuth::Lite::VERSION); } my $self = bless { ua => $ua, }, $class; $self->_init(%args); $self; } sub _init { my ($self, %args) = @_; my $signature_method_class = exists $args{signature_method} ? $args{signature_method} : 'HMAC_SHA1'; $signature_method_class =~ s/-/_/g; $signature_method_class = join('::', 'OAuth::Lite::SignatureMethod', $signature_method_class ); $signature_method_class->require or Carp::croak( sprintf qq/Could't find signature method class, %s/, $signature_method_class ); $self->{consumer_key} = $args{consumer_key} || ''; $self->{consumer_secret} = $args{consumer_secret} || ''; $self->{http_method} = $args{http_method} || 'POST'; $self->{auth_method} = $args{auth_method} || AUTH_HEADER; unless ( OAuth::Lite::AuthMethod->validate_method( $self->{auth_method} ) ) { Carp::croak( sprintf qq/Invalid auth method "%s"./, $self->{auth_method} ); } $self->{signature_method} = $signature_method_class; $self->{realm} = $args{realm}; $self->{site} = $args{site}; $self->{request_token_path} = $args{request_token_path}; $self->{access_token_path} = $args{access_token_path}; $self->{authorize_path} = $args{authorize_path}; $self->{callback_url} = $args{callback_url}; $self->{oauth_request} = undef; $self->{oauth_response} = undef; $self->{use_request_body_hash} = $args{use_request_body_hash} ? 1 : 0; $self->{_nonce} = $args{_nonce}; $self->{_timestamp} = $args{_timestamp}; }
sub request_token_url { my $self = shift; $self->{request_token_path} =~ m!^http(?:s)?\://! ? $self->{request_token_path} : sprintf q{%s%s}, $self->{site}, $self->{request_token_path}; }
sub access_token_url { my $self = shift; $self->{access_token_path} =~ m!^http(?:s)?\://! ? $self->{access_token_path} : sprintf q{%s%s}, $self->{site}, $self->{access_token_path}; }
sub authorization_url { my $self = shift; $self->{authorize_path} =~ m!^http(?:s)?\://! ? $self->{authorize_path} : sprintf q{%s%s}, $self->{site}, $self->{authorize_path}; }
sub url_to_authorize { my ($self, %args) = @_; $args{url} ||= $self->authorization_url; my $url = $args{url} or Carp::croak qq/url_to_authorize needs url./; my %params = (); if (defined $args{token}) { my $token = $args{token}; $params{oauth_token} = ( eval { $token->isa('OAuth::Lite::Token') } ) ? $token->token : $token; } $url = URI->new($url); $url->query_form(%params); $url->as_string; }
sub obtain_request_token { my $self = shift; my $res = $self->_get_request_token(@_); unless ($res->is_success) { return $self->error($res->status_line); } my $resp = OAuth::Lite::Response->from_encoded($res->decoded_content||$res->content); return $self->error(qq/oauth_callback_confirmed is not true/) unless $resp && $resp->token && $resp->token->callback_confirmed; $self->request_token($resp->token); $resp; }
sub get_request_token { my $self = shift; my $res = $self->_get_request_token(@_); unless ($res->is_success) { return $self->error($res->status_line); } my $token = OAuth::Lite::Token->from_encoded($res->decoded_content||$res->content); return $self->error(qq/oauth_callback_confirmed is not true/) unless $token && $token->callback_confirmed; $self->request_token($token); $token; } sub _get_request_token { my ($self, %args) = @_; $args{url} ||= $self->request_token_url; my $request_token_url = delete $args{url} or Carp::croak qq/get_request_token needs url in hash params or set request_token_path on constructor./; my $realm = delete $args{realm} || $self->{realm} || ''; my $callback = delete $args{callback_url} || $self->{callback_url} || 'oob'; my $res = $self->__request( realm => $realm, url => $request_token_url, params => {%args, oauth_callback => $callback}, ); return $res; }
sub obtain_access_token { my ($self, %args) = @_; $args{url} ||= $self->access_token_url; my $access_token_url = $args{url} or Carp::croak qq/get_access_token needs access_token_url./; my $realm = $args{realm} || $self->{realm} || ''; my $token = defined $args{token} ? $args{token} : undef; my $params = $args{params} || {}; my $res = $self->__request( realm => $realm, url => $access_token_url, token => $token, params => $params, ); unless ($res->is_success) { return $self->error($res->status_line); } my $resp = OAuth::Lite::Response->from_encoded($res->decoded_content||$res->content); $self->access_token($resp->token); $resp; }
sub get_access_token { my ($self, %args) = @_; $args{url} ||= $self->access_token_url; my $access_token_url = $args{url} or Carp::croak qq/get_access_token needs access_token_url./; my $token = defined $args{token} ? $args{token} : $self->request_token; Carp::croak qq/get_access_token needs token./ unless defined $token; my $realm = $args{realm} || $self->{realm} || ''; my $verifier = $args{verifier} or Carp::croak qq/verfier not found./; my $res = $self->__request( realm => $realm, url => $access_token_url, token => $token, params => { oauth_verifier => $verifier }, ); unless ($res->is_success) { return $self->error($res->status_line); } my $access_token = OAuth::Lite::Token->from_encoded($res->decoded_content||$res->content); $self->access_token($access_token); $access_token; }
sub gen_oauth_request { my ($self, %args) = @_; my $method = $args{method} || $self->{http_method}; my $url = $args{url}; my $content = $args{content}; my $token = $args{token}; my $extra = $args{params} || {}; my $realm = $args{realm} || $self->{realm} || $self->find_realm_from_last_response || ''; if (ref $extra eq 'ARRAY') { my %hash; for (0...scalar(@$extra)/2-1) { my $key = $extra->[$_ * 2]; my $value = $extra->[$_ * 2 + 1]; $hash{$key} ||= []; push @{ $hash{$key} }, $value; } $extra = \%hash; } my $headers = $args{headers}; if (defined $headers) { if (ref($headers) eq 'ARRAY') { $headers = HTTP::Headers->new(@$headers); } else { $headers = $headers->clone; } } else { $headers = HTTP::Headers->new; } my @send_data_methods = qw/POST PUT/; my @non_send_data_methods = qw/GET HEAD DELETE/; my $is_send_data_method = any { $method eq $_ } @send_data_methods; my $auth_method = $self->{auth_method}; $auth_method = AUTH_HEADER if ( !$is_send_data_method && $auth_method eq POST_BODY ); if ($auth_method eq URL_QUERY) { if ( $is_send_data_method && !$content ) { Carp::croak qq(You must set content-body in case you use combination of URL_QUERY and POST/PUT http method); } else { if ( $is_send_data_method ) { if ( my $hash = $self->build_body_hash($content) ) { $extra->{oauth_body_hash} = $hash; } } my $query = $self->gen_auth_query($method, $url, $token, $extra); $url = sprintf q{%s?%s}, $url, $query; } } elsif ($auth_method eq POST_BODY) { my $query = $self->gen_auth_query($method, $url, $token, $extra); $content = $query; $headers->header('Content-Type', q{application/x-www-form-urlencoded}); } else { my $origin_url = $url; my $copied_params = {}; for my $param_key ( keys %$extra ) { next if $param_key =~ /^x?oauth_/; $copied_params->{$param_key} = $extra->{$param_key}; } if ( keys %$copied_params > 0 ) { my $data = normalize_params($copied_params); if ( $is_send_data_method && !$content ) { $content = $data; } else { $url = sprintf q{%s?%s}, $url, $data; } } if ( $is_send_data_method ) { if ( my $hash = $self->build_body_hash($content) ) { $extra->{oauth_body_hash} = $hash; } } my $header = $self->gen_auth_header($method, $origin_url, { realm => $realm, token => $token, extra => $extra }); $headers->header( Authorization => $header ); } if ( $is_send_data_method ) { $headers->header('Content-Type', q{application/x-www-form-urlencoded}) unless $headers->header('Content-Type'); $headers->header('Content-Length', bytes::length($content) ); } my $req = HTTP::Request->new( $method, $url, $headers, $content ); $req; }
sub request { my ($self, %args) = @_; $args{token} ||= $self->access_token; $self->__request(%args); } sub __request { my ($self, %args) = @_; my $req = $self->gen_oauth_request(%args); $self->oauth_clear(); $self->oauth_request($req); my $res = $self->{ua}->request($req); $self->oauth_response($res); $res; }
sub get { my ( $self, $url, $args ) = @_; $args ||= {}; $args->{method} = 'GET'; $args->{url} = $url; $self->request(%$args); }
sub post { my ( $self, $url, $content, $args ) = @_; $args ||= {}; $args->{method} = 'POST'; $args->{url} = $url; $args->{content} = $content; $self->request(%$args); }
sub put { my ( $self, $url, $content, $args ) = @_; $args ||= {}; $args->{method} = 'PUT'; $args->{url} = $url; $args->{content} = $content; $self->request(%$args); }
sub delete { my ( $self, $url, $args ) = @_; $args ||= {}; $args->{method} = 'DELETE'; $args->{url} = $url; $self->request(%$args); }
sub find_realm_from_last_response { my $self = shift; return unless $self->oauth_response; my $authenticate = $self->oauth_response->header('WWW-Authenticate'); return unless ($authenticate && $authenticate =~ /^\s*OAuth/); my $realm = parse_auth_header($authenticate); $realm; }
sub gen_auth_header { my ($self, $method, $url, $args) = @_; my $extra = $args->{extra} || {}; my $params = $self->gen_auth_params($method, $url, $args->{token}, $extra); my $realm = $args->{realm} || ''; my $authorization_header = build_auth_header($realm, $params); $authorization_header; }
sub gen_auth_query { my ($self, $method, $url, $token, $extra) = @_; $extra ||= {}; my $params = $self->gen_auth_params($method, $url, $token, $extra); my %all = (%$extra, %$params); normalize_params({%all}); }
sub gen_auth_params { my ($self, $method, $url, $token, $extra) = @_; my $params = {}; $extra ||= {}; $params->{oauth_consumer_key} = $self->consumer_key || ''; $params->{oauth_timestamp} = $self->{_timestamp} || time(); $params->{oauth_nonce} = $self->{_nonce} || gen_random_key(); $params->{oauth_version} = $OAuth::Lite::OAUTH_DEFAULT_VERSION; my $token_secret = ''; if (defined $token) { if (eval { $token->isa('OAuth::Lite::Token') }) { $params->{oauth_token} = $token->token; $token_secret = $token->secret; } else { $params->{oauth_token} = $token; } } my $consumer_secret = $self->consumer_secret || ''; $params->{oauth_signature_method} = $self->{signature_method}->method_name; if ($params->{oauth_signature_method} eq 'PLAINTEXT' && lc($url) !~ /^https/) { warn qq(PLAINTEXT signature method should be used on SSL/TSL.); } $params = {%$params, %$extra}; my $base = create_signature_base_string($method, $url, $params); $params->{oauth_signature} = $self->{signature_method}->new( consumer_secret => $consumer_secret, token_secret => $token_secret, )->sign($base); $params; }
sub oauth_clear { my $self = shift; $self->{oauth_request} = undef; $self->{oauth_response} = undef; }
sub build_body_hash { my ( $self, $content ) = @_; if ( $self->{use_request_body_hash} ) { my $hash = $self->{signature_method}->build_body_hash($content); return $hash; } return; }
1;