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