How about we do our commits in trunk, so that we actually get sane linear revision...
[catagits/Catalyst-Authentication-Credential-OpenID.git] / lib / Catalyst / Authentication / Credential / OpenID.pm
CommitLineData
e5b6823d 1package Catalyst::Authentication::Credential::OpenID;
d214d0c0 2use strict;
788804c2 3use warnings; no warnings "uninitialized"; # for testing, not production 321
bf16184a 4use parent "Class::Accessor::Fast";
e5b6823d 5
6BEGIN {
7 __PACKAGE__->mk_accessors(qw/ _config realm debug secret /);
8}
9
b8dc66f3 10our $VERSION = "0.14";
e5b6823d 11
12use Net::OpenID::Consumer;
e5b6823d 13use Catalyst::Exception ();
14
15sub new : method {
16 my ( $class, $config, $c, $realm ) = @_;
17 my $self = { _config => { %{ $config },
18 %{ $realm->{config} }
19 }
20 };
21 bless $self, $class;
22
bf16184a 23 # 2.0 spec says "SHOULD" be named "openid_identifier."
e5b6823d 24 $self->_config->{openid_field} ||= "openid_identifier";
788804c2 25
26 $self->debug( $self->_config->{debug} );
27
28 my $secret = $self->_config->{consumer_secret} ||= join("+",
29 __PACKAGE__,
30 $VERSION,
31 sort keys %{ $c->config }
32 );
33
34 $secret = substr($secret,0,255) if length $secret > 255;
35 $self->secret($secret);
36 $self->_config->{ua_class} ||= "LWPx::ParanoidAgent";
37
38 my $agent_class = $self->_config->{ua_class};
39 eval "require $agent_class"
40 or Catalyst::Exception->throw("Could not 'require' user agent class " .
41 $self->_config->{ua_class});
42
43 $c->log->debug("Setting consumer secret: " . $secret) if $self->debug;
44
45 return $self;
46}
47
48sub authenticate : method {
49 my ( $self, $c, $realm, $authinfo ) = @_;
50
51 $c->log->debug("authenticate() called from " . $c->request->uri) if $self->debug;
52return 1;
53 my $field = $self->{_config}->{openid_field};
54
55 my $claimed_uri = $authinfo->{ $field };
56
57 # Its security related so we want to be explicit about GET/POST param retrieval.
58 $claimed_uri ||= $c->req->method eq 'GET' ?
59 $c->req->query_params->{ $field } : $c->req->body_params->{ $field };
60
61 my $csr = Net::OpenID::Consumer->new(
62 ua => $self->_config->{ua_class}->new(%{$self->_config->{ua_args} || {}}),
63 args => $c->req->params,
64 consumer_secret => $self->secret,
65 );
66
67 if ( $claimed_uri )
68 {
69 my $current = $c->uri_for($c->req->uri->path); # clear query/fragment...
70
71 my $identity = $csr->claimed_identity($claimed_uri)
72 or Catalyst::Exception->throw($csr->err);
73
74 $identity->set_extension_args(@{$self->_config->{extension_args}})
75 if $self->_config->{extension_args};
76
77 my $check_url = $identity->check_url(
78 return_to => $current . '?openid-check=1',
79 trust_root => $current,
80 delayed_return => 1,
81 );
82 $c->res->redirect($check_url);
83 $c->detach();
84 }
85 elsif ( $c->req->params->{'openid-check'} )
86 {
87 if ( my $setup_url = $csr->user_setup_url )
88 {
89 $c->res->redirect($setup_url);
90 $c->detach();
91 }
92 elsif ( $csr->user_cancel )
93 {
94 return;
95 }
96 elsif ( my $identity = $csr->verified_identity )
97 {
98 # This is where we ought to build an OpenID user and verify against the spec.
99 my $user = +{ map { $_ => scalar $identity->$_ }
100 qw( url display rss atom foaf declared_rss declared_atom declared_foaf foafmaker ) };
101
102 for(keys %{$self->{_config}->{extensions}}) {
103 $user->{extensions}->{$_} = $identity->signed_extension_fields($_);
104 }
105
106 my $user_obj = $realm->find_user($user, $c);
107
108 if ( ref $user_obj )
109 {
110 return $user_obj;
111 }
112 else
113 {
114 $c->log->debug("Verified OpenID identity failed to load with find_user; bad user_class? Try 'Null.'") if $c->debug;
115 return;
116 }
117 }
118 else
119 {
120 Catalyst::Exception->throw("Error validating identity: " .
121 $csr->err);
122 }
123 }
124 return;
125}
126
1271;
128
129__END__
130
131=head1 NAME
132
133Catalyst::Authentication::Credential::OpenID - OpenID credential for Catalyst::Plugin::Authentication framework.
134
135=head1 VERSION
136
1370.14
138
139=head1 SYNOPSIS
140
141In MyApp.pm-
142
143 use Catalyst qw/
144 Authentication
145 Session
146 Session::Store::FastMmap
147 Session::State::Cookie
148 /;
149
150Somewhere in myapp.conf-
151
152 <Plugin::Authentication>
153 default_realm openid
154 <realms>
155 <openid>
156 <credential>
157 class OpenID
158 </credential>
159 ua_class LWPx::ParanoidAgent
160 </openid>
161 </realms>
162 </Plugin::Authentication>
163
164Or in your myapp.yml if you're using L<YAML> instead-
165
166 Plugin::Authentication:
167 default_realm: openid
168 realms:
169 openid:
170 credential:
171 class: OpenID
172 ua_class: LWPx::ParanoidAgent
173
174In a controller, perhaps C<Root::openid>-
175
176 sub openid : Local {
177 my($self, $c) = @_;
178
179 if ( $c->authenticate() )
180 {
181 $c->flash(message => "You signed in with OpenID!");
182 $c->res->redirect( $c->uri_for('/') );
183 }
184 else
185 {
186 # Present OpenID form.
187 }
188 }
189
190And a L<Template> to match in C<openid.tt>-
191
192 <form action="[% c.uri_for('/openid') %]" method="GET" name="openid">
193 <input type="text" name="openid_identifier" class="openid" />
194 <input type="submit" value="Sign in with OpenID" />
195 </form>
196
197=head1 DESCRIPTION
198
199This is the B<third> OpenID related authentication piece for
200L<Catalyst>. The first E<mdash> L<Catalyst::Plugin::Authentication::OpenID>
201by Benjamin Trott E<mdash> was deprecated by the second E<mdash>
202L<Catalyst::Plugin::Authentication::Credential::OpenID> by Tatsuhiko
203Miyagawa E<mdash> and this is an attempt to deprecate both by conforming to
204the newish, at the time of this module's inception, realm-based
205authentication in L<Catalyst::Plugin::Authentication>.
206
207 1. Catalyst::Plugin::Authentication::OpenID
208 2. Catalyst::Plugin::Authentication::Credential::OpenID
209 3. Catalyst::Authentication::Credential::OpenID
210
211The benefit of this version is that you can use an arbitrary number of
212authentication systems in your L<Catalyst> application and configure
213and call all of them in the same way.
214
215Note that both earlier versions of OpenID authentication use the method
216C<authenticate_openid()>. This module uses C<authenticate()> and
217relies on you to specify the realm. You can specify the realm as the
218default in the configuration or inline with each
219C<authenticate()> call; more below.
220
221This module functions quite differently internally from the others.
222See L<Catalyst::Plugin::Authentication::Internals> for more about this
223implementation.
224
225=head1 METHODS
226
227=over 4
228
229=item $c->authenticate({},"your_openid_realm");
230
231Call to authenticate the user via OpenID. Returns false if
232authorization is unsuccessful. Sets the user into the session and
233returns the user object if authentication succeeds.
234
235You can see in the call above that the authentication hash is empty.
236The implicit OpenID parameter is, as the 2.0 specification says it
237SHOULD be, B<openid_identifier>. You can set it anything you like in
238your realm configuration, though, under the key C<openid_field>. If
239you call C<authenticate()> with the empty info hash and no configured
240C<openid_field> then only C<openid_identifier> is checked.
241
242It implicitly does this (sort of, it checks the request method too)-
243
244 my $claimed_uri = $c->req->params->{openid_identifier};
245 $c->authenticate({openid_identifier => $claimed_uri});
246
247=item Catalyst::Authentication::Credential::OpenID->new()
248
249You will never call this. Catalyst does it for you. The only important
250thing you might like to know about it is that it merges its realm
251configuration with its configuration proper. If this doesn't mean
252anything to you, don't worry.
253
254=back
255
256=head2 USER METHODS
257
258Currently the only supported user class is L<Catalyst::Plugin::Authentication::User::Hash>.
259
260=over 4
261
262=item $c->user->url
263
264=item $c->user->display
265
266=item $c->user->rss
267
268=item $c->user->atom
269
270=item $c->user->foaf
271
272=item $c->user->declared_rss
273
274=item $c->user->declared_atom
275
276=item $c->user->declared_foaf
277
278=item $c->user->foafmaker
279
280=back
281
282See L<Net::OpenID::VerifiedIdentity> for details.
283
284=head1 CONFIGURATION
285
286Catalyst authentication is now configured entirely from your
287application's configuration. Do not, for example, put
288C<Credential::OpenID> into your C<use Catalyst ...> statement.
289Instead, tell your application that in one of your authentication
290realms you will use the credential.
291
292In your application the following will give you two different
293authentication realms. One called "members" which authenticates with
294clear text passwords and one called "openid" which uses... uh, OpenID.
295
296 __PACKAGE__->config
297 ( name => "MyApp",
298 "Plugin::Authentication" => {
299 default_realm => "members",
300 realms => {
301 members => {
302 credential => {
303 class => "Password",
304 password_field => "password",
305 password_type => "clear"
306 },
307 store => {
308 class => "Minimal",
309 users => {
310 paco => {
311 password => "l4s4v3n7ur45",
312 },
313 }
314 }
315 },
316 openid => {
317 consumer_secret => "Don't bother setting",
318 ua_class => "LWPx::ParanoidAgent",
319 ua_args => {
320 whitelisted_hosts => [qw/ 127.0.0.1 localhost /],
321 },
322 credential => {
323 class => "OpenID",
324 store => {
325 class => "OpenID",
326 },
327 },
328 extension_args => [
329 'http://openid.net/extensions/sreg/1.1',
330 {
331 required => 'email',
332 optional => 'fullname,nickname,timezone',
333 },
334 ],
335 },
336 },
337 }
338 );
339
340This is the same configuration in the default L<Catalyst> configuration format from L<Config::General>.
341
342 name MyApp
343 <Plugin::Authentication>
344 default_realm members
345 <realms>
346 <members>
347 <store>
348 class Minimal
349 <users>
350 <paco>
351 password l4s4v3n7ur45
352 </paco>
353 </users>
354 </store>
355 <credential>
356 password_field password
357 password_type clear
358 class Password
359 </credential>
360 </members>
361 <openid>
362 <ua_args>
363 whitelisted_hosts 127.0.0.1
364 whitelisted_hosts localhost
365 </ua_args>
366 consumer_secret Don't bother setting
367 ua_class LWPx::ParanoidAgent
368 <credential>
369 <store>
370 class OpenID
371 </store>
372 class OpenID
373 </credential>
374 <extension_args>
375 http://openid.net/extensions/sreg/1.1
376 required email
377 optional fullname,nickname,timezone
378 </extension_args>
379 </openid>
380 </realms>
381 </Plugin::Authentication>
382
383And now, the same configuration in L<YAML>. B<NB>: L<YAML> is whitespace sensitive.
384
385 name: MyApp
386 Plugin::Authentication:
387 default_realm: members
388 realms:
389 members:
390 credential:
391 class: Password
392 password_field: password
393 password_type: clear
394 store:
395 class: Minimal
396 users:
397 paco:
398 password: l4s4v3n7ur45
399 openid:
400 credential:
401 class: OpenID
402 store:
403 class: OpenID
404 consumer_secret: Don't bother setting
405 ua_class: LWPx::ParanoidAgent
406 ua_args:
407 whitelisted_hosts:
408 - 127.0.0.1
409 - localhost
410 extension_args:
411 - http://openid.net/extensions/sreg/1.1
412 - required: email
413 optional: fullname,nickname,timezone
414
415B<NB>: There is no OpenID store yet.
416
417=head2 EXTENSIONS TO OPENID
418
419The L<Simple Registration|http://openid.net/extensions/sreg/1.1> (SREG) extension to OpenID is supported in the L<Net::OpenID> family now. Experimental support for it is included here as of v0.12. SREG is the only supported extension in OpenID 1.1. It's experimental in the sense it's a new interface and barely tested. Support for OpenID extensions is here to stay.
420
421=head2 MORE ON CONFIGURATION
422
423These are set in your realm. See above.
424
425=over 4
426
427=item ua_args and ua_class
428
429L<LWPx::ParanoidAgent> is the default agent E<mdash> C<ua_class>. You don't
430have to set it. I recommend that you do B<not> override it. You can
431with any well behaved L<LWP::UserAgent>. You probably should not.
432L<LWPx::ParanoidAgent> buys you many defenses and extra security
433checks. When you allow your application users freedom to initiate
434external requests, you open a big avenue for DoS (denial of service)
435attacks. L<LWPx::ParanoidAgent> defends against this.
436L<LWP::UserAgent> and any regular subclass of it will not.
437
438=item consumer_secret
439
440The underlying L<Net::OpenID::Consumer> object is seeded with a
441secret. If it's important to you to set your own, you can. The default
442uses this package name + its version + the sorted configuration keys
443of your Catalyst application (chopped at 255 characters if it's
444longer). This should generally be superior to any fixed string.
445
446=back
447
448=head1 TODO
449
450Support more of the new methods in the L<Net::OpenID> kit.
451
452There are some interesting implications with this sort of setup. Does
453a user aggregate realms or can a user be signed in under more than one
454realm? The documents could contain a recipe of the self-answering
455OpenID end-point that is in the tests.
456
457Debug statements need to be both expanded and limited via realm
458configuration.
459
460Better diagnostics in errors. Debug info at all consumer calls.
461
462Roles from provider domains? Mapped? Direct? A generic "openid" auto_role?
463
464=head1 THANKS
465
466To Benjamin Trott (L<Catalyst::Plugin::Authentication::OpenID>), Tatsuhiko Miyagawa (L<Catalyst::Plugin::Authentication::Credential::OpenID>), Brad Fitzpatrick for the great OpenID stuff, Martin Atkins for picking up the code to handle OpenID 2.0, and Jay Kuri and everyone else who has made Catalyst such a wonderful framework.
467
468L<Menno Blom|http://search.cpan.org/~blom/> provided a bug fix and the hook to use OpenID extensions.
469
470=head1 LICENSE AND COPYRIGHT
471
472Copyright (c) 2008, Ashley Pond V C<< <ashley@cpan.org> >>. Some of Tatsuhiko Miyagawa's work is reused here.
473
474This module is free software; you can redistribute it and modify it under the same terms as Perl itself. See L<perlartistic>.
475
476=head1 DISCLAIMER OF WARRANTY
477
478Because this software is licensed free of charge, there is no warranty
479for the software, to the extent permitted by applicable law. Except when
480otherwise stated in writing the copyright holders and other parties
481provide the software "as is" without warranty of any kind, either
482expressed or implied, including, but not limited to, the implied
483warranties of merchantability and fitness for a particular purpose. The
484entire risk as to the quality and performance of the software is with
485you. Should the software prove defective, you assume the cost of all
486necessary servicing, repair, or correction.
487
488In no event unless required by applicable law or agreed to in writing
489will any copyright holder, or any other party who may modify or
490redistribute the software as permitted by the above license, be
491liable to you for damages, including any general, special, incidental,
492or consequential damages arising out of the use or inability to use
493the software (including but not limited to loss of data or data being
494rendered inaccurate or losses sustained by you or third parties or a
495failure of the software to operate with any other software), even if
496such holder or other party has been advised of the possibility of
497such damages.
498
499=head1 SEE ALSO
500
501=over 4
502
503=item OpenID
504
505L<Net::OpenID::Server>, L<Net::OpenID::VerifiedIdentity>, L<Net::OpenID::Consumer>, L<http://openid.net/>, L<http://openid.net/developers/specs/>, and L<http://openid.net/extensions/sreg/1.1>.
506
507=item Catalyst Authentication
508
509L<Catalyst>, L<Catalyst::Plugin::Authentication>, L<Catalyst::Manual::Tutorial::Authorization>, and L<Catalyst::Manual::Tutorial::Authentication>.
510
511=item Catalyst Configuration
512
513L<Catalyst::Plugin::ConfigLoader>, L<Config::General>, and L<YAML>.
514
515=item Miscellaneous
516
517L<Catalyst::Manual::Tutorial>, L<Template>, L<LWPx::ParanoidAgent>.
518
519=back
520
521=cut
522package Catalyst::Authentication::Credential::OpenID;
523use strict;
524# use warnings; no warnings "uninitialized"; # for testing, not production
525use parent "Class::Accessor::Fast";
526
527BEGIN {
528 __PACKAGE__->mk_accessors(qw/ _config realm debug secret /);
529}
530
531our $VERSION = "0.13";
532
533use Net::OpenID::Consumer;
534use Catalyst::Exception ();
535
536sub new : method {
537 my ( $class, $config, $c, $realm ) = @_;
538 my $self = { _config => { %{ $config },
539 %{ $realm->{config} }
540 }
541 };
542 bless $self, $class;
543
544 # 2.0 spec says "SHOULD" be named "openid_identifier."
545 $self->_config->{openid_field} ||= "openid_identifier";
e5b6823d 546
547 $self->debug( $self->_config->{debug} );
548
549 my $secret = $self->_config->{consumer_secret} ||= join("+",
550 __PACKAGE__,
551 $VERSION,
552 sort keys %{ $c->config }
553 );
554
555 $secret = substr($secret,0,255) if length $secret > 255;
a404fd1a 556 $self->secret($secret);
bf16184a 557 $self->_config->{ua_class} ||= "LWPx::ParanoidAgent";
e5b6823d 558
ab944aad 559 my $agent_class = $self->_config->{ua_class};
560 eval "require $agent_class"
561 or Catalyst::Exception->throw("Could not 'require' user agent class " .
562 $self->_config->{ua_class});
e5b6823d 563
564 $c->log->debug("Setting consumer secret: " . $secret) if $self->debug;
565
566 return $self;
567}
568
569sub authenticate : method {
570 my ( $self, $c, $realm, $authinfo ) = @_;
571
572 $c->log->debug("authenticate() called from " . $c->request->uri) if $self->debug;
573
574 my $field = $self->{_config}->{openid_field};
575
576 my $claimed_uri = $authinfo->{ $field };
577
578 # Its security related so we want to be explicit about GET/POST param retrieval.
579 $claimed_uri ||= $c->req->method eq 'GET' ?
580 $c->req->query_params->{ $field } : $c->req->body_params->{ $field };
581
582 my $csr = Net::OpenID::Consumer->new(
583 ua => $self->_config->{ua_class}->new(%{$self->_config->{ua_args} || {}}),
584 args => $c->req->params,
bf16184a 585 consumer_secret => $self->secret,
e5b6823d 586 );
587
588 if ( $claimed_uri )
589 {
590 my $current = $c->uri_for($c->req->uri->path); # clear query/fragment...
591
592 my $identity = $csr->claimed_identity($claimed_uri)
593 or Catalyst::Exception->throw($csr->err);
594
a404fd1a 595 $identity->set_extension_args(@{$self->_config->{extension_args}})
596 if $self->_config->{extension_args};
597
e5b6823d 598 my $check_url = $identity->check_url(
599 return_to => $current . '?openid-check=1',
600 trust_root => $current,
601 delayed_return => 1,
602 );
603 $c->res->redirect($check_url);
85db8ed7 604 $c->detach();
e5b6823d 605 }
606 elsif ( $c->req->params->{'openid-check'} )
607 {
608 if ( my $setup_url = $csr->user_setup_url )
609 {
610 $c->res->redirect($setup_url);
611 return;
612 }
613 elsif ( $csr->user_cancel )
614 {
615 return;
616 }
617 elsif ( my $identity = $csr->verified_identity )
618 {
619 # This is where we ought to build an OpenID user and verify against the spec.
620 my $user = +{ map { $_ => scalar $identity->$_ }
621 qw( url display rss atom foaf declared_rss declared_atom declared_foaf foafmaker ) };
a404fd1a 622
623 for(keys %{$self->{_config}->{extensions}}) {
624 $user->{extensions}->{$_} = $identity->signed_extension_fields($_);
625 }
e5b6823d 626
627 my $user_obj = $realm->find_user($user, $c);
628
629 if ( ref $user_obj )
630 {
631 return $user_obj;
632 }
633 else
634 {
635 $c->log->debug("Verified OpenID identity failed to load with find_user; bad user_class? Try 'Null.'") if $c->debug;
636 return;
637 }
638 }
639 else
640 {
641 Catalyst::Exception->throw("Error validating identity: " .
642 $csr->err);
643 }
644 }
d214d0c0 645 return;
e5b6823d 646}
647
6481;
649
650__END__
651
e5b6823d 652=head1 NAME
653
ab944aad 654Catalyst::Authentication::Credential::OpenID - OpenID credential for Catalyst::Plugin::Authentication framework.
e5b6823d 655
d214d0c0 656=head1 VERSION
657
995b096c 6580.13
d214d0c0 659
e5b6823d 660=head1 SYNOPSIS
661
ab944aad 662In MyApp.pm-
d214d0c0 663
e5b6823d 664 use Catalyst qw/
665 Authentication
666 Session
667 Session::Store::FastMmap
668 Session::State::Cookie
669 /;
670
ab944aad 671Somewhere in myapp.conf-
d214d0c0 672
673 <Plugin::Authentication>
674 default_realm openid
675 <realms>
676 <openid>
d214d0c0 677 <credential>
d214d0c0 678 class OpenID
679 </credential>
f29585f9 680 ua_class LWPx::ParanoidAgent
d214d0c0 681 </openid>
682 </realms>
683 </Plugin::Authentication>
684
ab944aad 685Or in your myapp.yml if you're using L<YAML> instead-
d214d0c0 686
e5b6823d 687 Plugin::Authentication:
688 default_realm: openid
689 realms:
690 openid:
691 credential:
692 class: OpenID
d214d0c0 693 ua_class: LWPx::ParanoidAgent
694
ab944aad 695In a controller, perhaps C<Root::openid>-
e5b6823d 696
bf16184a 697 sub openid : Local {
e5b6823d 698 my($self, $c) = @_;
699
700 if ( $c->authenticate() )
701 {
702 $c->flash(message => "You signed in with OpenID!");
703 $c->res->redirect( $c->uri_for('/') );
704 }
705 else
706 {
707 # Present OpenID form.
708 }
bf16184a 709 }
e5b6823d 710
ab944aad 711And a L<Template> to match in C<openid.tt>-
d214d0c0 712
bf16184a 713 <form action="[% c.uri_for('/openid') %]" method="GET" name="openid">
714 <input type="text" name="openid_identifier" class="openid" />
715 <input type="submit" value="Sign in with OpenID" />
716 </form>
e5b6823d 717
e5b6823d 718=head1 DESCRIPTION
719
720This is the B<third> OpenID related authentication piece for
6342195d 721L<Catalyst>. The first E<mdash> L<Catalyst::Plugin::Authentication::OpenID>
722by Benjamin Trott E<mdash> was deprecated by the second E<mdash>
e5b6823d 723L<Catalyst::Plugin::Authentication::Credential::OpenID> by Tatsuhiko
6342195d 724Miyagawa E<mdash> and this is an attempt to deprecate both by conforming to
e5b6823d 725the newish, at the time of this module's inception, realm-based
726authentication in L<Catalyst::Plugin::Authentication>.
727
d214d0c0 728 1. Catalyst::Plugin::Authentication::OpenID
729 2. Catalyst::Plugin::Authentication::Credential::OpenID
730 3. Catalyst::Authentication::Credential::OpenID
e5b6823d 731
732The benefit of this version is that you can use an arbitrary number of
733authentication systems in your L<Catalyst> application and configure
734and call all of them in the same way.
735
d214d0c0 736Note that both earlier versions of OpenID authentication use the method
e5b6823d 737C<authenticate_openid()>. This module uses C<authenticate()> and
738relies on you to specify the realm. You can specify the realm as the
739default in the configuration or inline with each
740C<authenticate()> call; more below.
741
742This module functions quite differently internally from the others.
743See L<Catalyst::Plugin::Authentication::Internals> for more about this
744implementation.
745
a404fd1a 746=head1 METHODS
e5b6823d 747
748=over 4
749
d214d0c0 750=item $c->authenticate({},"your_openid_realm");
e5b6823d 751
752Call to authenticate the user via OpenID. Returns false if
753authorization is unsuccessful. Sets the user into the session and
754returns the user object if authentication succeeds.
755
756You can see in the call above that the authentication hash is empty.
757The implicit OpenID parameter is, as the 2.0 specification says it
758SHOULD be, B<openid_identifier>. You can set it anything you like in
759your realm configuration, though, under the key C<openid_field>. If
760you call C<authenticate()> with the empty info hash and no configured
761C<openid_field> then only C<openid_identifier> is checked.
762
763It implicitly does this (sort of, it checks the request method too)-
764
765 my $claimed_uri = $c->req->params->{openid_identifier};
766 $c->authenticate({openid_identifier => $claimed_uri});
767
d214d0c0 768=item Catalyst::Authentication::Credential::OpenID->new()
bf16184a 769
770You will never call this. Catalyst does it for you. The only important
771thing you might like to know about it is that it merges its realm
772configuration with its configuration proper. If this doesn't mean
773anything to you, don't worry.
e5b6823d 774
bf16184a 775=back
e5b6823d 776
777=head2 USER METHODS
778
779Currently the only supported user class is L<Catalyst::Plugin::Authentication::User::Hash>.
780
bf16184a 781=over 4
e5b6823d 782
d214d0c0 783=item $c->user->url
e5b6823d 784
d214d0c0 785=item $c->user->display
e5b6823d 786
d214d0c0 787=item $c->user->rss
e5b6823d 788
d214d0c0 789=item $c->user->atom
e5b6823d 790
d214d0c0 791=item $c->user->foaf
e5b6823d 792
d214d0c0 793=item $c->user->declared_rss
e5b6823d 794
d214d0c0 795=item $c->user->declared_atom
e5b6823d 796
d214d0c0 797=item $c->user->declared_foaf
e5b6823d 798
d214d0c0 799=item $c->user->foafmaker
e5b6823d 800
801=back
802
803See L<Net::OpenID::VerifiedIdentity> for details.
804
805=head1 CONFIGURATION
806
807Catalyst authentication is now configured entirely from your
808application's configuration. Do not, for example, put
809C<Credential::OpenID> into your C<use Catalyst ...> statement.
810Instead, tell your application that in one of your authentication
811realms you will use the credential.
812
813In your application the following will give you two different
814authentication realms. One called "members" which authenticates with
815clear text passwords and one called "openid" which uses... uh, OpenID.
816
817 __PACKAGE__->config
818 ( name => "MyApp",
819 "Plugin::Authentication" => {
820 default_realm => "members",
821 realms => {
822 members => {
823 credential => {
824 class => "Password",
825 password_field => "password",
826 password_type => "clear"
827 },
828 store => {
829 class => "Minimal",
830 users => {
831 paco => {
832 password => "l4s4v3n7ur45",
833 },
834 }
835 }
836 },
837 openid => {
bf16184a 838 consumer_secret => "Don't bother setting",
e5b6823d 839 ua_class => "LWPx::ParanoidAgent",
840 ua_args => {
841 whitelisted_hosts => [qw/ 127.0.0.1 localhost /],
842 },
843 credential => {
844 class => "OpenID",
845 store => {
846 class => "OpenID",
847 },
848 },
a404fd1a 849 extension_args => [
850 'http://openid.net/extensions/sreg/1.1',
851 {
852 required => 'email',
853 optional => 'fullname,nickname,timezone',
854 },
855 ],
e5b6823d 856 },
857 },
a404fd1a 858 }
859 );
e5b6823d 860
d214d0c0 861This is the same configuration in the default L<Catalyst> configuration format from L<Config::General>.
862
863 name MyApp
864 <Plugin::Authentication>
865 default_realm members
866 <realms>
867 <members>
868 <store>
869 class Minimal
870 <users>
871 <paco>
872 password l4s4v3n7ur45
873 </paco>
874 </users>
875 </store>
876 <credential>
877 password_field password
878 password_type clear
879 class Password
880 </credential>
881 </members>
882 <openid>
883 <ua_args>
884 whitelisted_hosts 127.0.0.1
885 whitelisted_hosts localhost
886 </ua_args>
887 consumer_secret Don't bother setting
888 ua_class LWPx::ParanoidAgent
889 <credential>
890 <store>
891 class OpenID
892 </store>
893 class OpenID
894 </credential>
a404fd1a 895 <extension_args>
896 http://openid.net/extensions/sreg/1.1
897 required email
898 optional fullname,nickname,timezone
899 </extension_args>
d214d0c0 900 </openid>
901 </realms>
902 </Plugin::Authentication>
903
904And now, the same configuration in L<YAML>. B<NB>: L<YAML> is whitespace sensitive.
e5b6823d 905
906 name: MyApp
907 Plugin::Authentication:
908 default_realm: members
909 realms:
910 members:
911 credential:
912 class: Password
913 password_field: password
914 password_type: clear
915 store:
916 class: Minimal
917 users:
918 paco:
919 password: l4s4v3n7ur45
920 openid:
921 credential:
922 class: OpenID
923 store:
924 class: OpenID
bf16184a 925 consumer_secret: Don't bother setting
e5b6823d 926 ua_class: LWPx::ParanoidAgent
927 ua_args:
928 whitelisted_hosts:
929 - 127.0.0.1
930 - localhost
a404fd1a 931 extension_args:
932 - http://openid.net/extensions/sreg/1.1
933 - required: email
934 optional: fullname,nickname,timezone
e5b6823d 935
6342195d 936B<NB>: There is no OpenID store yet.
bf16184a 937
a404fd1a 938=head2 EXTENSIONS TO OPENID
939
940The L<Simple Registration|http://openid.net/extensions/sreg/1.1> (SREG) extension to OpenID is supported in the L<Net::OpenID> family now. Experimental support for it is included here as of v0.12. SREG is the only supported extension in OpenID 1.1. It's experimental in the sense it's a new interface and barely tested. Support for OpenID extensions is here to stay.
941
d214d0c0 942=head2 MORE ON CONFIGURATION
bf16184a 943
944These are set in your realm. See above.
945
946=over 4
947
d214d0c0 948=item ua_args and ua_class
bf16184a 949
6342195d 950L<LWPx::ParanoidAgent> is the default agent E<mdash> C<ua_class>. You don't
bf16184a 951have to set it. I recommend that you do B<not> override it. You can
952with any well behaved L<LWP::UserAgent>. You probably should not.
e5b6823d 953L<LWPx::ParanoidAgent> buys you many defenses and extra security
954checks. When you allow your application users freedom to initiate
955external requests, you open a big avenue for DoS (denial of service)
956attacks. L<LWPx::ParanoidAgent> defends against this.
957L<LWP::UserAgent> and any regular subclass of it will not.
958
d214d0c0 959=item consumer_secret
bf16184a 960
961The underlying L<Net::OpenID::Consumer> object is seeded with a
962secret. If it's important to you to set your own, you can. The default
963uses this package name + its version + the sorted configuration keys
964of your Catalyst application (chopped at 255 characters if it's
965longer). This should generally be superior to any fixed string.
966
967=back
968
e5b6823d 969=head1 TODO
970
a404fd1a 971Support more of the new methods in the L<Net::OpenID> kit.
972
e5b6823d 973There are some interesting implications with this sort of setup. Does
974a user aggregate realms or can a user be signed in under more than one
975realm? The documents could contain a recipe of the self-answering
976OpenID end-point that is in the tests.
977
978Debug statements need to be both expanded and limited via realm
979configuration.
980
bf16184a 981Better diagnostics in errors. Debug info at all consumer calls.
e5b6823d 982
bf16184a 983Roles from provider domains? Mapped? Direct? A generic "openid" auto_role?
e5b6823d 984
f29585f9 985=head1 THANKS
986
a404fd1a 987To Benjamin Trott (L<Catalyst::Plugin::Authentication::OpenID>), Tatsuhiko Miyagawa (L<Catalyst::Plugin::Authentication::Credential::OpenID>), Brad Fitzpatrick for the great OpenID stuff, Martin Atkins for picking up the code to handle OpenID 2.0, and Jay Kuri and everyone else who has made Catalyst such a wonderful framework.
988
995b096c 989L<Menno Blom|http://search.cpan.org/~blom/> provided a bug fix and the hook to use OpenID extensions.
f29585f9 990
e5b6823d 991=head1 LICENSE AND COPYRIGHT
992
a404fd1a 993Copyright (c) 2008, Ashley Pond V C<< <ashley@cpan.org> >>. Some of Tatsuhiko Miyagawa's work is reused here.
e5b6823d 994
a404fd1a 995This module is free software; you can redistribute it and modify it under the same terms as Perl itself. See L<perlartistic>.
e5b6823d 996
e5b6823d 997=head1 DISCLAIMER OF WARRANTY
998
999Because this software is licensed free of charge, there is no warranty
1000for the software, to the extent permitted by applicable law. Except when
1001otherwise stated in writing the copyright holders and other parties
1002provide the software "as is" without warranty of any kind, either
1003expressed or implied, including, but not limited to, the implied
1004warranties of merchantability and fitness for a particular purpose. The
1005entire risk as to the quality and performance of the software is with
1006you. Should the software prove defective, you assume the cost of all
1007necessary servicing, repair, or correction.
1008
1009In no event unless required by applicable law or agreed to in writing
1010will any copyright holder, or any other party who may modify or
1011redistribute the software as permitted by the above license, be
1012liable to you for damages, including any general, special, incidental,
1013or consequential damages arising out of the use or inability to use
1014the software (including but not limited to loss of data or data being
1015rendered inaccurate or losses sustained by you or third parties or a
1016failure of the software to operate with any other software), even if
1017such holder or other party has been advised of the possibility of
1018such damages.
1019
e5b6823d 1020=head1 SEE ALSO
1021
d214d0c0 1022=over 4
e5b6823d 1023
d214d0c0 1024=item OpenID
e5b6823d 1025
a404fd1a 1026L<Net::OpenID::Server>, L<Net::OpenID::VerifiedIdentity>, L<Net::OpenID::Consumer>, L<http://openid.net/>, L<http://openid.net/developers/specs/>, and L<http://openid.net/extensions/sreg/1.1>.
e5b6823d 1027
d214d0c0 1028=item Catalyst Authentication
1029
1030L<Catalyst>, L<Catalyst::Plugin::Authentication>, L<Catalyst::Manual::Tutorial::Authorization>, and L<Catalyst::Manual::Tutorial::Authentication>.
1031
1200a1ee 1032=item Catalyst Configuration
d214d0c0 1033
1034L<Catalyst::Plugin::ConfigLoader>, L<Config::General>, and L<YAML>.
1035
1036=item Miscellaneous
1037
f29585f9 1038L<Catalyst::Manual::Tutorial>, L<Template>, L<LWPx::ParanoidAgent>.
d214d0c0 1039
1040=back
1041
e5b6823d 1042=cut