package Catalyst::Authentication::Credential::OpenID;
use strict;
# use warnings; no warnings "uninitialized"; # for testing, not production
use parent "Class::Accessor::Fast";
BEGIN {
__PACKAGE__->mk_accessors(qw/ _config realm debug secret /);
}
our $VERSION = "0.13";
use Net::OpenID::Consumer;
use Catalyst::Exception ();
sub new : method {
my ( $class, $config, $c, $realm ) = @_;
my $self = { _config => { %{ $config },
%{ $realm->{config} }
}
};
bless $self, $class;
# 2.0 spec says "SHOULD" be named "openid_identifier."
$self->_config->{openid_field} ||= "openid_identifier";
$self->debug( $self->_config->{debug} );
my $secret = $self->_config->{consumer_secret} ||= join("+",
__PACKAGE__,
$VERSION,
sort keys %{ $c->config }
);
$secret = substr($secret,0,255) if length $secret > 255;
$self->secret($secret);
$self->_config->{ua_class} ||= "LWPx::ParanoidAgent";
my $agent_class = $self->_config->{ua_class};
eval "require $agent_class"
or Catalyst::Exception->throw("Could not 'require' user agent class " .
$self->_config->{ua_class});
$c->log->debug("Setting consumer secret: " . $secret) if $self->debug;
return $self;
}
sub authenticate : method {
my ( $self, $c, $realm, $authinfo ) = @_;
$c->log->debug("authenticate() called from " . $c->request->uri) if $self->debug;
my $field = $self->{_config}->{openid_field};
my $claimed_uri = $authinfo->{ $field };
# Its security related so we want to be explicit about GET/POST param retrieval.
$claimed_uri ||= $c->req->method eq 'GET' ?
$c->req->query_params->{ $field } : $c->req->body_params->{ $field };
my $csr = Net::OpenID::Consumer->new(
ua => $self->_config->{ua_class}->new(%{$self->_config->{ua_args} || {}}),
args => $c->req->params,
consumer_secret => $self->secret,
);
if ( $claimed_uri )
{
my $current = $c->uri_for($c->req->uri->path); # clear query/fragment...
my $identity = $csr->claimed_identity($claimed_uri)
or Catalyst::Exception->throw($csr->err);
$identity->set_extension_args(@{$self->_config->{extension_args}})
if $self->_config->{extension_args};
my $check_url = $identity->check_url(
return_to => $current . '?openid-check=1',
trust_root => $current,
delayed_return => 1,
);
$c->res->redirect($check_url);
$c->detach();
}
elsif ( $c->req->params->{'openid-check'} )
{
if ( my $setup_url = $csr->user_setup_url )
{
$c->res->redirect($setup_url);
return;
}
elsif ( $csr->user_cancel )
{
return;
}
elsif ( my $identity = $csr->verified_identity )
{
# This is where we ought to build an OpenID user and verify against the spec.
my $user = +{ map { $_ => scalar $identity->$_ }
qw( url display rss atom foaf declared_rss declared_atom declared_foaf foafmaker ) };
for(keys %{$self->{_config}->{extensions}}) {
$user->{extensions}->{$_} = $identity->signed_extension_fields($_);
}
my $user_obj = $realm->find_user($user, $c);
if ( ref $user_obj )
{
return $user_obj;
}
else
{
$c->log->debug("Verified OpenID identity failed to load with find_user; bad user_class? Try 'Null.'") if $c->debug;
return;
}
}
else
{
Catalyst::Exception->throw("Error validating identity: " .
$csr->err);
}
}
return;
}
1;
__END__
=head1 NAME
Catalyst::Authentication::Credential::OpenID - OpenID credential for Catalyst::Plugin::Authentication framework.
=head1 VERSION
0.13
=head1 SYNOPSIS
In MyApp.pm-
use Catalyst qw/
Authentication
Session
Session::Store::FastMmap
Session::State::Cookie
/;
Somewhere in myapp.conf-
default_realm openid
class OpenID
ua_class LWPx::ParanoidAgent
Or in your myapp.yml if you're using L instead-
Plugin::Authentication:
default_realm: openid
realms:
openid:
credential:
class: OpenID
ua_class: LWPx::ParanoidAgent
In a controller, perhaps C-
sub openid : Local {
my($self, $c) = @_;
if ( $c->authenticate() )
{
$c->flash(message => "You signed in with OpenID!");
$c->res->redirect( $c->uri_for('/') );
}
else
{
# Present OpenID form.
}
}
And a L to match in C-
=head1 DESCRIPTION
This is the B OpenID related authentication piece for
L. The first E L
by Benjamin Trott E was deprecated by the second E
L by Tatsuhiko
Miyagawa E and this is an attempt to deprecate both by conforming to
the newish, at the time of this module's inception, realm-based
authentication in L.
1. Catalyst::Plugin::Authentication::OpenID
2. Catalyst::Plugin::Authentication::Credential::OpenID
3. Catalyst::Authentication::Credential::OpenID
The benefit of this version is that you can use an arbitrary number of
authentication systems in your L application and configure
and call all of them in the same way.
Note that both earlier versions of OpenID authentication use the method
C. This module uses C and
relies on you to specify the realm. You can specify the realm as the
default in the configuration or inline with each
C call; more below.
This module functions quite differently internally from the others.
See L for more about this
implementation.
=head1 METHODS
=over 4
=item $c->authenticate({},"your_openid_realm");
Call to authenticate the user via OpenID. Returns false if
authorization is unsuccessful. Sets the user into the session and
returns the user object if authentication succeeds.
You can see in the call above that the authentication hash is empty.
The implicit OpenID parameter is, as the 2.0 specification says it
SHOULD be, B. You can set it anything you like in
your realm configuration, though, under the key C. If
you call C with the empty info hash and no configured
C then only C is checked.
It implicitly does this (sort of, it checks the request method too)-
my $claimed_uri = $c->req->params->{openid_identifier};
$c->authenticate({openid_identifier => $claimed_uri});
=item Catalyst::Authentication::Credential::OpenID->new()
You will never call this. Catalyst does it for you. The only important
thing you might like to know about it is that it merges its realm
configuration with its configuration proper. If this doesn't mean
anything to you, don't worry.
=back
=head2 USER METHODS
Currently the only supported user class is L.
=over 4
=item $c->user->url
=item $c->user->display
=item $c->user->rss
=item $c->user->atom
=item $c->user->foaf
=item $c->user->declared_rss
=item $c->user->declared_atom
=item $c->user->declared_foaf
=item $c->user->foafmaker
=back
See L for details.
=head1 CONFIGURATION
Catalyst authentication is now configured entirely from your
application's configuration. Do not, for example, put
C into your C