Version 0.03
[catagits/Catalyst-Authentication-Credential-OAuth.git] / lib / Catalyst / Authentication / Credential / OAuth.pm
CommitLineData
fc19db6b 1package Catalyst::Authentication::Credential::OAuth;
fc19db6b 2use Moose;
05873c3d 3use MooseX::Types::Moose qw/ Bool HashRef /;
4use MooseX::Types::Common::String qw/ NonEmptySimpleStr /;
fc19db6b 5use Net::OAuth;
6#$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
fc19db6b 7use LWP::UserAgent;
05873c3d 8use HTTP::Request::Common;
fc19db6b 9use String::Random qw/ random_string /;
fc19db6b 10use Catalyst::Exception ();
05873c3d 11use namespace::autoclean;
fc19db6b 12
54aac345 13our $VERSION = '0.03';
7d722318 14
05873c3d 15has debug => ( is => 'ro', isa => Bool );
16has providers => ( is => 'ro', isa => HashRef, required => 1 );
17has ua => ( is => 'ro', lazy_build => 1, init_arg => undef, isa => 'LWP::UserAgent' );
fc19db6b 18
05873c3d 19sub BUILDARGS {
20 my ($self, $config, $c, $realm) = @_;
fc19db6b 21
05873c3d 22 return $config;
23}
fc19db6b 24
05873c3d 25sub BUILD {
26 my ($self) = @_;
27 $self->ua; # Ensure lazy value is built.
fc19db6b 28}
29
05873c3d 30sub _build_ua {
31 my $self = shift;
2cd406c8 32
33 LWP::UserAgent->new;
05873c3d 34}
fc19db6b 35
36sub authenticate {
37 my ($self, $c, $realm, $auth_info) = @_;
fc19db6b 38
05873c3d 39 Catalyst::Exception->throw( "Provider is not defined." )
40 unless defined $auth_info->{provider} || defined $self->providers->{ $auth_info->{provider} };
fc19db6b 41
05873c3d 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 );
fc19db6b 59
60 $c->log_debug( "authenticate() called from " . $c->request->uri ) if $self->debug;
61
05873c3d 62 my $oauth_token = $c->req->method eq 'GET'
63 ? $c->req->query_params->{oauth_token}
64 : $c->req->body_params->{oauth_token};
fc19db6b 65
05873c3d 66 if( $oauth_token ) {
fc19db6b 67
05873c3d 68 my $response = Net::OAuth->response( 'user auth' )->from_hash( $c->req->params );
fc19db6b 69
70 my $request = Net::OAuth->request( 'access token' )->new(
05873c3d 71 %defaults,
fc19db6b 72 token => $response->token,
73 token_secret => '',
05873c3d 74 request_url => $provider->{access_token_endpoint},
54aac345 75 verifier => $c->req->params->{oauth_verifier},
fc19db6b 76 );
fc19db6b 77 $request->sign;
78
05873c3d 79 my $ua_response = $self->ua->request( GET $request->to_url );
fc19db6b 80 Catalyst::Exception->throw( $ua_response->status_line.' '.$ua_response->content )
81 unless $ua_response->is_success;
05873c3d 82
fc19db6b 83 $response = Net::OAuth->response( 'access token' )->from_post_body( $ua_response->content );
84
2cd406c8 85 my $user = +{
fc19db6b 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 {
fc19db6b 100 my $request = Net::OAuth->request( 'request token' )->new(
05873c3d 101 %defaults,
102 request_url => $provider->{request_token_endpoint}
fc19db6b 103 );
fc19db6b 104 $request->sign;
105
05873c3d 106 my $ua_response = $self->ua->request( GET $request->to_url );
107
fc19db6b 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
fc19db6b 113 $request = Net::OAuth->request( 'user auth' )->new(
05873c3d 114 %defaults,
fc19db6b 115 token => $response->token,
116 );
117
05873c3d 118 $c->res->redirect( $request->to_url( $provider->{user_auth_endpoint} ) );
fc19db6b 119 }
120
121}
122
123
124
1251;
126
127
128__END__
129
130=head1 NAME
131
132Catalyst::Authentication::Credential::OAuth - OAuth credential for Catalyst::Plugin::Authentication framework.
133
134=head1 VERSION
135
7d722318 1360.02
fc19db6b 137
138=head1 SYNOPSIS
139
140In MyApp.pm
141
05873c3d 142 use Catalyst qw/
143 Authentication
144 Session
145 Session::Store::FastMmap
146 Session::State::Cookie
147 /;
fc19db6b 148
149
150In myapp.conf
151
05873c3d 152 <Plugin::Authentication>
153 default_realm oauth
154 <realms>
155 <oauth>
fc19db6b 156 <credential>
05873c3d 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>
fc19db6b 171
172
173In controller code,
174
05873c3d 175 sub oauth : Local {
176 my ($self, $c) = @_;
fc19db6b 177
05873c3d 178 if( $c->authenticate( { provider => 'example.com' } ) ) {
179 #do something with $c->user
180 }
181 }
fc19db6b 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
05873c3d 193=item $c->user->extra_params - whatever other params the provider sends back
fc19db6b 194
195=back
196
197=head1 AUTHOR
198
199Cosmin Budrica E<lt>cosmin@sinapticode.comE<gt>
200
201Bogdan Lucaciu E<lt>bogdan@sinapticode.comE<gt>
202
a726a7ce 203With contributions from:
204
205 Tomas Doran E<lt>bobtfish@bobtfish.netE</gt>
206
207
208=head1 BUGS
209
210Only tested with twitter
211
fc19db6b 212=head1 COPYRIGHT
213
2cd406c8 214Copyright (c) 2009 Sinapticode. All rights reserved
fc19db6b 215
216This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
217
218=cut