File Coverage

blib/lib/Catalyst/Authentication/Credential/OAuth.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Catalyst::Authentication::Credential::OAuth;
2 1     1   1737 use Moose;
  0            
  0            
3             use MooseX::Types::Moose qw/ Bool HashRef /;
4             use MooseX::Types::Common::String qw/ NonEmptySimpleStr /;
5             use Net::OAuth;
6             #$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
7             use LWP::UserAgent;
8             use HTTP::Request::Common;
9             use String::Random qw/ random_string /;
10             use Catalyst::Exception ();
11             use namespace::autoclean;
12              
13             our $VERSION = '0.04';
14              
15             has debug => ( is => 'ro', isa => Bool );
16             has providers => ( is => 'ro', isa => HashRef, required => 1 );
17             has ua => ( is => 'ro', lazy_build => 1, init_arg => undef, isa => 'LWP::UserAgent' );
18              
19             sub BUILDARGS {
20             my ($self, $config, $c, $realm) = @_;
21              
22             return $config;
23             }
24              
25             sub BUILD {
26             my ($self) = @_;
27             $self->ua; # Ensure lazy value is built.
28             }
29              
30             sub _build_ua {
31             my $self = shift;
32              
33             LWP::UserAgent->new;
34             }
35              
36             sub authenticate {
37             my ($self, $c, $realm, $auth_info) = @_;
38              
39             Catalyst::Exception->throw( "Provider is not defined." )
40             unless defined $auth_info->{provider} || defined $self->providers->{ $auth_info->{provider} };
41              
42             my $provider = $self->providers->{ $auth_info->{provider} };
43              
44             for ( qw/ consumer_key consumer_secret request_token_endpoint access_token_endpoint user_auth_endpoint / ) {
45             Catalyst::Exception->throw( $_ . " is not defined for provider ". $auth_info->{provider} )
46             unless $provider->{$_};
47             }
48              
49             my %defaults = (
50             consumer_key => $provider->{consumer_key},
51             consumer_secret => $provider->{consumer_secret},
52             timestamp => time,
53             nonce => random_string( 'ccccccccccccccccccc' ),
54             request_method => 'GET',
55             signature_method => 'HMAC-SHA1',
56             oauth_version => '1.0a',
57             callback => $c->uri_for( $c->action, $c->req->captures, @{ $c->req->args } )->as_string
58             );
59              
60             $c->log_debug( "authenticate() called from " . $c->request->uri ) if $self->debug;
61              
62             my $oauth_token = $c->req->method eq 'GET'
63             ? $c->req->query_params->{oauth_token}
64             : $c->req->body_params->{oauth_token};
65              
66             if( $oauth_token ) {
67              
68             my $response = Net::OAuth->response( 'user auth' )->from_hash( $c->req->params );
69              
70             my $request = Net::OAuth->request( 'access token' )->new(
71             %defaults,
72             token => $response->token,
73             token_secret => '',
74             request_url => $provider->{access_token_endpoint},
75             verifier => $c->req->params->{oauth_verifier},
76             );
77             $request->sign;
78              
79             my $ua_response = $self->ua->request( GET $request->to_url );
80             Catalyst::Exception->throw( $ua_response->status_line.' '.$ua_response->content )
81             unless $ua_response->is_success;
82              
83             $response = Net::OAuth->response( 'access token' )->from_post_body( $ua_response->content );
84              
85             my $user = +{
86             token => $response->token,
87             token_secret => $response->token_secret,
88             extra_params => $response->extra_params
89             };
90              
91             my $user_obj = $realm->find_user( $user, $c );
92              
93             return $user_obj if ref $user_obj;
94              
95             $c->log->debug( 'Verified OAuth identity failed' ) if $self->debug;
96              
97             return;
98             }
99             else {
100             my $request = Net::OAuth->request( 'request token' )->new(
101             %defaults,
102             request_url => $provider->{request_token_endpoint}
103             );
104             $request->sign;
105              
106             my $ua_response = $self->ua->request( GET $request->to_url );
107              
108             Catalyst::Exception->throw( $ua_response->status_line.' '.$ua_response->content )
109             unless $ua_response->is_success;
110              
111             my $response = Net::OAuth->response( 'request token' )->from_post_body( $ua_response->content );
112              
113             $request = Net::OAuth->request( 'user auth' )->new(
114             %defaults,
115             token => $response->token,
116             );
117              
118             $c->res->redirect( $request->to_url( $provider->{user_auth_endpoint} ) );
119             }
120              
121             }
122              
123              
124              
125             1;
126              
127              
128             __END__
129              
130             =head1 NAME
131              
132             Catalyst::Authentication::Credential::OAuth - OAuth credential for Catalyst::Plugin::Authentication framework.
133              
134             =head1 VERSION
135              
136             0.02
137              
138             =head1 SYNOPSIS
139              
140             In MyApp.pm
141              
142             use Catalyst qw/
143             Authentication
144             Session
145             Session::Store::FastMmap
146             Session::State::Cookie
147             /;
148              
149              
150             In myapp.conf
151              
152             <Plugin::Authentication>
153             default_realm oauth
154             <realms>
155             <oauth>
156             <credential>
157             class OAuth
158             <providers>
159             <example.com>
160             consumer_key my_app_key
161             consumer_secret my_app_secret
162             request_token_endpoint http://example.com/oauth/request_token
163             access_token_endpoint http://example.com/oauth/access_token
164             user_auth_endpoint http://example.com/oauth/authorize
165             </example.com>
166             </providers>
167             </credential>
168             </oauth>
169             </realms>
170             </Plugin::Authentication>
171              
172              
173             In controller code,
174              
175             sub oauth : Local {
176             my ($self, $c) = @_;
177              
178             if( $c->authenticate( { provider => 'example.com' } ) ) {
179             #do something with $c->user
180             }
181             }
182              
183              
184              
185             =head1 USER METHODS
186              
187             =over 4
188              
189             =item $c->user->token
190              
191             =item $c->user->token_secret
192              
193             =item $c->user->extra_params - whatever other params the provider sends back
194              
195             =back
196              
197             =head1 AUTHOR
198              
199             Cosmin Budrica E<lt>cosmin@sinapticode.comE<gt>
200              
201             Bogdan Lucaciu E<lt>bogdan@sinapticode.comE<gt>
202              
203             With contributions from:
204              
205             Tomas Doran E<lt>bobtfish@bobtfish.netE</gt>
206              
207              
208             =head1 BUGS
209              
210             Only tested with twitter
211              
212             =head1 COPYRIGHT
213              
214             Copyright (c) 2009 Sinapticode. All rights reserved
215              
216             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
217              
218             =cut