Commit | Line | Data |
fc19db6b |
1 | package Catalyst::Authentication::Credential::OAuth; |
fc19db6b |
2 | use Moose; |
05873c3d |
3 | use MooseX::Types::Moose qw/ Bool HashRef /; |
4 | use MooseX::Types::Common::String qw/ NonEmptySimpleStr /; |
fc19db6b |
5 | use Net::OAuth; |
6 | #$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A; |
fc19db6b |
7 | use LWP::UserAgent; |
05873c3d |
8 | use HTTP::Request::Common; |
fc19db6b |
9 | use String::Random qw/ random_string /; |
fc19db6b |
10 | use Catalyst::Exception (); |
05873c3d |
11 | use namespace::autoclean; |
fc19db6b |
12 | |
05873c3d |
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' ); |
fc19db6b |
16 | |
05873c3d |
17 | sub BUILDARGS { |
18 | my ($self, $config, $c, $realm) = @_; |
fc19db6b |
19 | |
05873c3d |
20 | return $config; |
21 | } |
fc19db6b |
22 | |
05873c3d |
23 | sub BUILD { |
24 | my ($self) = @_; |
25 | $self->ua; # Ensure lazy value is built. |
fc19db6b |
26 | } |
27 | |
05873c3d |
28 | sub _build_ua { |
29 | my $self = shift; |
2cd406c8 |
30 | |
31 | LWP::UserAgent->new; |
05873c3d |
32 | } |
fc19db6b |
33 | |
34 | sub authenticate { |
35 | my ($self, $c, $realm, $auth_info) = @_; |
fc19db6b |
36 | |
05873c3d |
37 | Catalyst::Exception->throw( "Provider is not defined." ) |
38 | unless defined $auth_info->{provider} || defined $self->providers->{ $auth_info->{provider} }; |
fc19db6b |
39 | |
05873c3d |
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 | ); |
fc19db6b |
57 | |
58 | $c->log_debug( "authenticate() called from " . $c->request->uri ) if $self->debug; |
59 | |
05873c3d |
60 | my $oauth_token = $c->req->method eq 'GET' |
61 | ? $c->req->query_params->{oauth_token} |
62 | : $c->req->body_params->{oauth_token}; |
fc19db6b |
63 | |
05873c3d |
64 | if( $oauth_token ) { |
fc19db6b |
65 | |
05873c3d |
66 | my $response = Net::OAuth->response( 'user auth' )->from_hash( $c->req->params ); |
fc19db6b |
67 | |
68 | my $request = Net::OAuth->request( 'access token' )->new( |
05873c3d |
69 | %defaults, |
fc19db6b |
70 | token => $response->token, |
71 | token_secret => '', |
05873c3d |
72 | request_url => $provider->{access_token_endpoint}, |
fc19db6b |
73 | ); |
fc19db6b |
74 | $request->sign; |
75 | |
05873c3d |
76 | my $ua_response = $self->ua->request( GET $request->to_url ); |
fc19db6b |
77 | Catalyst::Exception->throw( $ua_response->status_line.' '.$ua_response->content ) |
78 | unless $ua_response->is_success; |
05873c3d |
79 | |
fc19db6b |
80 | $response = Net::OAuth->response( 'access token' )->from_post_body( $ua_response->content ); |
81 | |
2cd406c8 |
82 | my $user = +{ |
fc19db6b |
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 { |
fc19db6b |
97 | my $request = Net::OAuth->request( 'request token' )->new( |
05873c3d |
98 | %defaults, |
99 | request_url => $provider->{request_token_endpoint} |
fc19db6b |
100 | ); |
fc19db6b |
101 | $request->sign; |
102 | |
05873c3d |
103 | my $ua_response = $self->ua->request( GET $request->to_url ); |
104 | |
fc19db6b |
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 | |
fc19db6b |
110 | $request = Net::OAuth->request( 'user auth' )->new( |
05873c3d |
111 | %defaults, |
fc19db6b |
112 | token => $response->token, |
113 | ); |
114 | |
05873c3d |
115 | $c->res->redirect( $request->to_url( $provider->{user_auth_endpoint} ) ); |
fc19db6b |
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 | |
05873c3d |
139 | use Catalyst qw/ |
140 | Authentication |
141 | Session |
142 | Session::Store::FastMmap |
143 | Session::State::Cookie |
144 | /; |
fc19db6b |
145 | |
146 | |
147 | In myapp.conf |
148 | |
05873c3d |
149 | <Plugin::Authentication> |
150 | default_realm oauth |
151 | <realms> |
152 | <oauth> |
fc19db6b |
153 | <credential> |
05873c3d |
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> |
fc19db6b |
168 | |
169 | |
170 | In controller code, |
171 | |
05873c3d |
172 | sub oauth : Local { |
173 | my ($self, $c) = @_; |
fc19db6b |
174 | |
05873c3d |
175 | if( $c->authenticate( { provider => 'example.com' } ) ) { |
176 | #do something with $c->user |
177 | } |
178 | } |
fc19db6b |
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 | |
05873c3d |
190 | =item $c->user->extra_params - whatever other params the provider sends back |
fc19db6b |
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 | |
a726a7ce |
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 | |
fc19db6b |
209 | =head1 COPYRIGHT |
210 | |
2cd406c8 |
211 | Copyright (c) 2009 Sinapticode. All rights reserved |
fc19db6b |
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 |