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 | |
54aac345 |
13 | our $VERSION = '0.03'; |
7d722318 |
14 | |
05873c3d |
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' ); |
fc19db6b |
18 | |
05873c3d |
19 | sub BUILDARGS { |
20 | my ($self, $config, $c, $realm) = @_; |
fc19db6b |
21 | |
05873c3d |
22 | return $config; |
23 | } |
fc19db6b |
24 | |
05873c3d |
25 | sub BUILD { |
26 | my ($self) = @_; |
27 | $self->ua; # Ensure lazy value is built. |
fc19db6b |
28 | } |
29 | |
05873c3d |
30 | sub _build_ua { |
31 | my $self = shift; |
2cd406c8 |
32 | |
33 | LWP::UserAgent->new; |
05873c3d |
34 | } |
fc19db6b |
35 | |
36 | sub 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 | |
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 | |
7d722318 |
136 | 0.02 |
fc19db6b |
137 | |
138 | =head1 SYNOPSIS |
139 | |
140 | In MyApp.pm |
141 | |
05873c3d |
142 | use Catalyst qw/ |
143 | Authentication |
144 | Session |
145 | Session::Store::FastMmap |
146 | Session::State::Cookie |
147 | /; |
fc19db6b |
148 | |
149 | |
150 | In 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 | |
173 | In 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 | |
199 | Cosmin Budrica E<lt>cosmin@sinapticode.comE<gt> |
200 | |
201 | Bogdan Lucaciu E<lt>bogdan@sinapticode.comE<gt> |
202 | |
a726a7ce |
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 | |
fc19db6b |
212 | =head1 COPYRIGHT |
213 | |
2cd406c8 |
214 | Copyright (c) 2009 Sinapticode. All rights reserved |
fc19db6b |
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 |