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