Changeset 2173
- Timestamp:
- 04/30/08 23:58:44 (7 months ago)
- Files:
-
- branches/release-36/extlib/Net/OpenID/Association.pm (modified) (5 diffs)
- branches/release-36/extlib/Net/OpenID/ClaimedIdentity.pm (modified) (9 diffs)
- branches/release-36/extlib/Net/OpenID/Consumer.pm (modified) (29 diffs)
- branches/release-36/extlib/Net/OpenID/IndirectMessage.pm (added)
- branches/release-36/extlib/Net/OpenID/VerifiedIdentity.pm (modified) (8 diffs)
- branches/release-36/extlib/Net/OpenID/Yadis (added)
- branches/release-36/extlib/Net/OpenID/Yadis.pm (added)
- branches/release-36/extlib/Net/OpenID/Yadis/Service.pm (added)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/release-36/extlib/Net/OpenID/Association.pm
r1098 r2173 38 38 } 39 39 40 sub type { 41 my $self = shift; 42 die if @_; 43 $self->{'type'}; 44 } 45 40 46 sub server { 41 47 my Net::OpenID::Association $self = shift; … … 64 70 # a new assoc_handle if none is found, or has expired 65 71 sub server_assoc { 66 my ($csr, $server) = @_; 72 my ($csr, $server, $force_reassociate, %opts) = @_; 73 74 my $protocol_version = delete $opts{protocol_version} || 1; 75 Carp::croak("unknown options: " . join(", ", keys %opts)) if %opts; 67 76 68 77 # closure to return undef (dumb consumer mode) and log why … … 75 84 return $dumb->("no_cache") unless $cache; 76 85 77 # try first from cached association handle 78 if (my $handle = $cache->get("shandle:$server")) { 79 my $assoc = handle_assoc($csr, $server, $handle); 80 81 if ($assoc && $assoc->usable) { 82 $csr->_debug("Found association from cache (handle=$handle)"); 83 return $assoc; 86 unless ($force_reassociate) { 87 # try first from cached association handle 88 if (my $handle = $cache->get("shandle:$server")) { 89 my $assoc = handle_assoc($csr, $server, $handle); 90 91 if ($assoc && $assoc->usable) { 92 $csr->_debug("Found association from cache (handle=$handle)"); 93 return $assoc; 94 } 84 95 } 85 96 } … … 94 105 "openid.dh_consumer_public" => OpenID::util::bi2arg($dh->pub_key), 95 106 ); 107 108 if ($protocol_version == 2) { 109 $post{"openid.ns"} = OpenID::util::version_2_namespace(); 110 } 96 111 97 112 my $req = HTTP::Request->new(POST => $server); … … 160 175 $cache->set("shandle:$server", $ahandle); 161 176 177 # now we test that the cache object given to us actually works. if it 178 # doesn't, it'll also fail later, making the verify fail, so let's 179 # go into stateless (dumb mode) earlier if we can detect this. 180 $cache->get("shandle:$server") 181 or return $dumb->("cache_broken"); 182 162 183 return $assoc; 163 184 } branches/release-36/extlib/Net/OpenID/ClaimedIdentity.pm
r1098 r2173 5 5 package Net::OpenID::ClaimedIdentity; 6 6 use fields ( 7 'identity', # the canonical URL that was found, following redirects 8 'server', # author-identity identity server endpoint 9 'consumer', # ref up to the Net::OpenID::Consumer which generated us 10 'delegate', # the delegated URL actually asserted by the server 11 ); 7 'identity', # the canonical URL that was found, following redirects 8 'server', # author-identity identity server endpoint 9 'consumer', # ref up to the Net::OpenID::Consumer which generated us 10 'delegate', # the delegated URL actually asserted by the server 11 'protocol_version', # The version of the OpenID Authentication Protocol that is used 12 'semantic_info', # Stuff that we've discovered in the identifier page's metadata 13 'extension_args', # Extension arguments that the caller wants to add to the request 14 ); 12 15 13 16 sub new { … … 15 18 $self = fields::new( $self ) unless ref $self; 16 19 my %opts = @_; 17 for my $f (qw( identity server consumer delegate )) {20 for my $f (qw( identity server consumer delegate protocol_version semantic_info )) { 18 21 $self->{$f} = delete $opts{$f}; 22 } 23 24 $self->{protocol_version} ||= 1; 25 unless ($self->{protocol_version} == 1 || $self->{protocol_version} == 2) { 26 Carp::croak("Unsupported protocol version"); 19 27 } 20 28 … … 22 30 $self->{'identity'} =~ s!^(https?://.+?)(/(?:.*))?$!lc($1) . $2!ie; 23 31 32 $self->{extension_args} = {}; 33 24 34 Carp::croak("unknown options: " . join(", ", keys %opts)) if %opts; 25 35 return $self; … … 32 42 } 33 43 44 sub delegated_url { 45 my Net::OpenID::ClaimedIdentity $self = shift; 46 Carp::croak("Too many parameters") if @_; 47 return $self->{'delegate'}; 48 } 49 34 50 sub identity_server { 35 51 my Net::OpenID::ClaimedIdentity $self = shift; 36 52 Carp::croak("Too many parameters") if @_; 37 53 return $self->{server}; 54 } 55 56 sub protocol_version { 57 my Net::OpenID::ClaimedIdentity $self = shift; 58 Carp::croak("Too many parameters") if @_; 59 return $self->{protocol_version}; 60 } 61 62 sub semantic_info { 63 my Net::OpenID::ClaimedIdentity $self = shift; 64 Carp::croak("Too many parameters") if @_; 65 return $self->{semantic_info} if $self->{semantic_info}; 66 my $final_url = ''; 67 my $info = $self->{consumer}->_find_semantic_info($self->claimed_url, \$final_url); 68 # Don't return anything if the URL has changed. Something bad may be happening. 69 $info = {} if $final_url ne $self->claimed_url; 70 return $self->{semantic_info} = $info; 71 } 72 73 sub set_extension_args { 74 my Net::OpenID::ClaimedIdentity $self = shift; 75 my $ext_uri = shift; 76 my $args = shift; 77 Carp::croak("Too many parameters") if @_; 78 Carp::croak("No extension URI given") unless $ext_uri; 79 Carp::croak("Expecting hashref of args") if defined($args) && ref $args ne 'HASH'; 80 81 $self->{extension_args}{$ext_uri} = $args; 38 82 } 39 83 … … 45 89 my $trust_root = delete $opts{'trust_root'}; 46 90 my $delayed_ret = delete $opts{'delayed_return'}; 91 my $force_reassociate = delete $opts{'force_reassociate'}; 92 my $use_assoc_handle = delete $opts{'use_assoc_handle'}; 93 my $actually_return_association = delete $opts{'actually_return_association'}; 47 94 48 95 Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts; … … 55 102 56 103 # get an assoc (or undef for dumb mode) 57 my $assoc = Net::OpenID::Association::server_assoc($csr, $ident_server); 104 my $assoc; 105 if ($use_assoc_handle) { 106 $assoc = Net::OpenID::Association::handle_assoc($csr, $ident_server, $use_assoc_handle); 107 } else { 108 $assoc = Net::OpenID::Association::server_assoc($csr, $ident_server, $force_reassociate, ( 109 protocol_version => $self->protocol_version, 110 )); 111 } 112 113 # for the openid-test project: (doing interop testing) 114 if ($actually_return_association) { 115 return $assoc; 116 } 58 117 59 118 my $identity_arg = $self->{'delegate'} || $self->{'identity'}; 60 119 61 120 # make a note back to ourselves that we're using a delegate 62 if ($self->{'delegate'}) { 121 # but only in the 1.1 case because 2.0 has a core field for this 122 if ($self->{'delegate'} && $self->protocol_version == 1) { 63 123 OpenID::util::push_url_arg(\$return_to, 64 124 "oic.identity", $self->{identity}); … … 73 133 74 134 my $curl = $ident_server; 75 OpenID::util::push_url_arg(\$curl, 76 "openid.mode", ($delayed_ret ? "checkid_setup" : "checkid_immediate"), 77 "openid.identity", $identity_arg, 78 "openid.return_to", $return_to, 79 80 ($trust_root ? 81 ("openid.trust_root", $trust_root) : ()), 82 83 ($assoc ? 84 ("openid.assoc_handle", $assoc->handle) : ()), 85 ); 135 if ($self->protocol_version == 1) { 136 OpenID::util::push_url_arg(\$curl, 137 "openid.mode" => ($delayed_ret ? "checkid_setup" : "checkid_immediate"), 138 "openid.identity" => $identity_arg, 139 "openid.return_to" => $return_to, 140 141 ($trust_root ? ( 142 "openid.trust_root" => $trust_root 143 ) : ()), 144 145 ($assoc ? ( 146 "openid.assoc_handle" => $assoc->handle 147 ) : ()), 148 ); 149 } 150 elsif ($self->protocol_version == 2) { 151 # NOTE: OpenID Auth 2.0 uses different terminology for a bunch 152 # of things than 1.1 did. This library still uses the 1.1 terminology 153 # in its API. 154 OpenID::util::push_openid2_url_arg(\$curl, 155 "mode" => ($delayed_ret ? "checkid_setup" : "checkid_immediate"), 156 "claimed_id" => $self->claimed_url, 157 "identity" => $identity_arg, 158 "return_to" => $return_to, 159 160 ($trust_root ? ( 161 "realm" => $trust_root 162 ) : ()), 163 164 ($assoc ? ( 165 "assoc_handle" => $assoc->handle 166 ) : ()), 167 ); 168 } 169 170 # Finally we add in the extension arguments, if any 171 my %ext_url_args = (); 172 my $ext_idx = 1; 173 foreach my $ext_uri (keys %{$self->{extension_args}}) { 174 my $ext_alias; 175 176 if ($self->protocol_version >= 2) { 177 $ext_alias = 'e'.($ext_idx++); 178 $ext_url_args{'openid.ns.'.$ext_alias} = $ext_uri; 179 } 180 else { 181 # For OpenID 1.1 only the "SREG" extension is allowed, 182 # and it must use the "openid.sreg." prefix. 183 next unless $ext_uri eq "http://openid.net/extensions/sreg/1.1"; 184 $ext_alias = "sreg"; 185 } 186 187 foreach my $k (keys %{$self->{extension_args}{$ext_uri}}) { 188 $ext_url_args{'openid.'.$ext_alias.'.'.$k} = $self->{extension_args}{$ext_uri}{$k}; 189 } 190 } 191 OpenID::util::push_url_arg(\$curl, %ext_url_args) if %ext_url_args; 86 192 87 193 $self->{consumer}->_debug("check_url for (del=$self->{delegate}, id=$self->{identity}) = $curl"); … … 144 250 claimed identity is valid, and sign a message saying so. 145 251 252 =item $url = $cident->B<delegated_url> 253 254 If the claimed URL is using delegation, this returns the delegated identity that will 255 actually be sent to the identity server. 256 257 =item $version = $cident->B<protocol_version> 258 259 Determines whether this identifier is to be verified by OpenID 1.1 260 or by OpenID 2.0. Returns C<1> or C<2> respectively. This will 261 affect the way the C<check_url> is constructed. 262 263 =item $cident->B<set_extension_args>($ns_uri, $args) 264 265 If called before you access C<check_url>, the arguments given in the hashref 266 $args will be added to the request in the given extension namespace. 267 For example, to use the Simple Registration (SREG) extension: 268 269 $cident->set_extension_args( 270 'http://openid.net/extensions/sreg/1.1', 271 { 272 required => 'email', 273 optional => 'fullname,nickname', 274 policy_url => 'http://example.com/privacypolicy.html', 275 }, 276 ); 277 278 Note that when making an OpenID 1.1 request, only the Simple Registration 279 extension is supported. There was no general extension mechanism defined 280 in OpenID 1.1, so SREG (with the namespace URI as in the example above) 281 is supported as a special case. All other extension namespaces will 282 be silently ignored when making a 1.1 request. 283 146 284 =item $url = $cident->B<check_url>( %opts ) 147 285 … … 204 342 L<Net::OpenID::Server> 205 343 206 Website: L<http://www. danga.com/openid/>207 344 Website: L<http://www.openid.net/> 345 branches/release-36/extlib/Net/OpenID/Consumer.pm
r1098 r2173 10 10 11 11 use vars qw($VERSION); 12 $VERSION = "0.1 2";12 $VERSION = "0.14"; 13 13 14 14 use fields ( 15 'cache', # the Cache object sent to URI::Fetch 16 'ua', # LWP::UserAgent instance to use 17 'args', # how to get at your args 18 'consumer_secret', # scalar/subref 19 'required_root', # the default required_root value, or undef 20 'last_errcode', # last error code we got 21 'last_errtext', # last error code we got 22 'debug', # debug flag or codeblock 23 ); 15 'cache', # the Cache object sent to URI::Fetch 16 'ua', # LWP::UserAgent instance to use 17 'args', # how to get at your args 18 'message', # args interpreted as an IndirectMessage, if possible 19 'consumer_secret', # scalar/subref 20 'required_root', # the default required_root value, or undef 21 'last_errcode', # last error code we got 22 'last_errtext', # last error code we got 23 'debug', # debug flag or codeblock 24 'minimum_version', # The minimum protocol version to support 25 ); 24 26 25 27 use Net::OpenID::ClaimedIdentity; 26 28 use Net::OpenID::VerifiedIdentity; 27 29 use Net::OpenID::Association; 30 use Net::OpenID::Yadis; 31 use Net::OpenID::IndirectMessage; 28 32 29 33 use MIME::Base64 (); … … 38 42 my %opts = @_; 39 43 44 $opts{minimum_version} ||= 1; 45 40 46 $self->{ua} = delete $opts{ua}; 41 47 $self->args ( delete $opts{args} ); … … 43 49 $self->consumer_secret ( delete $opts{consumer_secret} ); 44 50 $self->required_root ( delete $opts{required_root} ); 51 $self->minimum_version ( delete $opts{minimum_version} ); 45 52 46 53 $self->{debug} = delete $opts{debug}; … … 48 55 Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts; 49 56 return $self; 57 } 58 59 # NOTE: This method is here only to support the openid-test library. 60 # Don't call it from anywhere else, or you'll break when it gets 61 # removed. Instead, set the minimum_version property. 62 # FIXME: Can we just make openid-test set minimum_version and get 63 # rid of this? 64 sub disable_version_1 { 65 my $self = shift; 66 $self->{minimum_version} = 2.0; 50 67 } 51 68 … … 53 70 sub consumer_secret { &_getset; } 54 71 sub required_root { &_getset; } 72 sub minimum_version { &_getset; } 55 73 56 74 sub _getset { … … 98 116 } elsif (ref $what eq "HASH") { 99 117 $getter = sub { $what->{$_[0]}; }; 100 } elsif ( ref $what eq "CGI") {118 } elsif (UNIVERSAL::isa($what, "CGI")) { 101 119 $getter = sub { scalar $what->param($_[0]); }; 102 120 } elsif (ref $what eq "Apache") { … … 112 130 if ($getter) { 113 131 $self->{args} = $getter; 132 $self->{message} = Net::OpenID::IndirectMessage->new($what, minimum_version => $self->minimum_version); 114 133 } 115 134 } 116 135 $self->{args}; 136 } 137 138 sub message { 139 my Net::OpenID::Consumer $self = shift; 140 if (my $key = shift) { 141 return $self->{message} ? $self->{message}->get($key) : undef; 142 } 143 else { 144 return $self->{message}; 145 } 146 } 147 148 sub _message_mode { 149 my $message = $_[0]->message; 150 return $message ? $message->mode : undef; 151 } 152 153 sub _message_version { 154 my $message = $_[0]->message; 155 return $message ? $message->protocol_version : undef; 117 156 } 118 157 … … 141 180 'no_head_tag' => "URL provided doesn't seem to have a head tag.", 142 181 'url_fetch_err' => "Error fetching the provided URL.", 182 'bad_mode' => "The openid.mode argument is not correct", 183 'protocol_version_incorrect' => "The provided URL uses the wrong protocol version", 184 'naive_verify_failed_return' => "Provider says signature is invalid", 185 'naive_verify_failed_network' => "Could not contact provider to verify signature", 143 186 }->{$code}; 144 187 … … 239 282 $val =~ m!\bhref=[\"\']([^\"\']+)[\"\']!i) { 240 283 $ret->{"openid.$temp"} = $1; 284 next; 285 } 286 287 # OpenID2 providers / local identifiers 288 # <link rel="openid2.provider" href="http://www.livejournal.com/misc/openid.bml" /> 289 if ($type eq "link" && 290 $val =~ /\brel=.openid2\.(provider|local_id)./i && ($temp = $1) && 291 $val =~ m!\bhref=[\"\']([^\"\']+)[\"\']!i) { 292 $ret->{"openid2.$temp"} = $1; 241 293 next; 242 294 } … … 302 354 } 303 355 304 $self->_debug("semantic info ($url) = " . join(", ", %$ret));356 $self->_debug("semantic info ($url) = " . join(", ", map { $_.' => '.$ret->{$_} } keys %$ret)) if $self->{debug}; 305 357 306 358 return $ret; … … 317 369 return $self->_fail("no_identity_server") unless $sem_info->{"openid.server"}; 318 370 $sem_info->{"openid.server"}; 371 } 372 373 sub is_server_response { 374 my Net::OpenID::Consumer $self = shift; 375 return $self->_message_mode ? 1 : 0; 376 } 377 378 sub handle_server_response { 379 my Net::OpenID::Consumer $self = shift; 380 my %callbacks_in = @_; 381 my %callbacks = (); 382 383 foreach my $cb (qw(not_openid setup_required cancelled verified error)) { 384 $callbacks{$cb} = delete($callbacks_in{$cb}) || sub { Carp::croak("No ".$cb." callback") }; 385 } 386 Carp::croak("Unknown callbacks ".join(',', keys %callbacks)) if %callbacks_in; 387 388 unless ($self->is_server_response) { 389 return $callbacks{not_openid}->(); 390 } 391 392 if (my $setup_url = $self->user_setup_url) { 393 return $callbacks{setup_required}->($setup_url); 394 } 395 elsif ($self->user_cancel) { 396 return $callbacks{cancelled}->(); 397 } 398 elsif (my $vident = $self->verified_identity) { 399 return $callbacks{verified}->($vident); 400 } 401 else { 402 return $callbacks{error}->($self->errcode, $self->errtext); 403 } 404 319 405 } 320 406 … … 334 420 return $self->_fail("bogus_url", "Invalid URL") unless $url =~ m!^https?://!i; 335 421 # add a slash, if none exists 336 $url .= "/" unless $url =~ m!^http ://.+/!i;422 $url .= "/" unless $url =~ m!^https?://.+/!i; 337 423 338 424 my $final_url; 339 425 340 my $sem_info = $self->_find_semantic_info($url, \$final_url) or 341 return; 342 343 my $id_server = $sem_info->{"openid.server"} or 344 return $self->_fail("no_identity_server"); 426 my $id_server; 427 my $delegate; 428 my $version; 429 my $sem_info = undef; 430 my $discovery_mechanism; 431 432 # TODO: Support XRI too? 433 434 # First we try Yadis service discovery 435 my $yadis = Net::OpenID::Yadis->new(ua => $self->{ua}); 436 if ($yadis->discover($url)) { 437 # FIXME: Currently we don't ever do _find_semantic_info in the Yadis 438 # code path, so an extra redundant HTTP request is done later 439 # when the semantic info is accessed. 440 441 $final_url = $yadis->identity_url; 442 my @services = $yadis->services( 443 OpenID::util::version_2_xrds_service_url(), 444 OpenID::util::version_2_xrds_directed_service_url(), 445 OpenID::util::version_1_xrds_service_url(), 446 ); 447 my $version2 = OpenID::util::version_2_xrds_service_url(); 448 my $version1 = OpenID::util::version_1_xrds_service_url(); 449 my $version2_directed = OpenID::util::version_2_xrds_directed_service_url(); 450 451 foreach my $service (@services) { 452 my $service_uri = $service->URI; 453 454 # Service->URI seems to return all sorts of bizarre things, so let's 455 # normalize it to always be a string. 456 if (ref($service_uri) eq 'ARRAY') { 457 my @sorted_id_servers = sort { $b->{priority} <=> $a->{priority} } @$service_uri; 458 $service_uri = $sorted_id_servers[0]; 459 } 460 if (ref($service_uri) eq 'HASH') { 461 $service_uri = $service_uri->{content}; 462 } 463 464 if (grep(/^${version2}$/, $service->Type)) { 465 # We have an OpenID 2.0 end-user identifier 466 $id_server = $service_uri; 467 $delegate = $service->extra_field("LocalID"); 468 $version = 2; 469 $discovery_mechanism = "Yadis"; 470 } 471 elsif (grep(/^${version1}$/, $service->Type)) { 472 # We have an OpenID 1.1 end-user identifier 473 $id_server = $service_uri; 474 $delegate = $service->extra_field("Delegate", "http://openid.net/xmlns/1.0"); 475 $version = 1; 476 $discovery_mechanism = "Yadis"; 477 } 478 elsif (grep(/^${version2_directed}$/, $service->Type)) { 479 # We have an OpenID 2.0 OP identifier (i.e. we're doing directed identity) 480 $id_server = $service_uri; 481 $version = 2; 482 # In this case, the user's claimed identifier is a magic value 483 # and the actual identifier will be determined by the provider. 484 $final_url = OpenID::util::version_2_identifier_select_url(); 485 $delegate = OpenID::util::version_2_identifier_select_url(); 486 $discovery_mechanism = "Yadis"; 487 } 488 } 489 } 490 491 # If Yadis didn't work out, we need to fall back on HTML-based discovery 492 unless ($id_server) { 493 $sem_info = $self->_find_semantic_info($url, \$final_url) or return; 494 495 if ($sem_info->{"openid2.provider"}) { 496 $id_server = $sem_info->{"openid2.provider"}; 497 $delegate = $sem_info->{"openid2.local_id"}; 498 $version = 2; 499 $discovery_mechanism = "HTML"; 500 } 501 elsif ($sem_info->{"openid.server"}) { 502 $id_server = $sem_info->{"openid.server"}; 503 $delegate = $sem_info->{"openid.delegate"}; 504 $version = 1; 505 $discovery_mechanism = "HTML"; 506 } 507 } 508 509 return $self->_fail("no_identity_server") unless $id_server; 510 return $self->_fail("protocol_version_incorrect") unless $version >= $self->minimum_version; 511 512 $self->_debug("Discovered version $version endpoint at $id_server via $discovery_mechanism"); 513 $self->_debug("Delegate is $delegate") if $delegate; 345 514 346 515 return Net::OpenID::ClaimedIdentity->new( 347 identity => $final_url, 348 server => $id_server, 349 consumer => $self, 350 delegate => $sem_info->{'openid.delegate'}, 351 ); 516 identity => $final_url, 517 server => $id_server, 518 consumer => $self, 519 delegate => $delegate, 520 protocol_version => $version, 521 semantic_info => $sem_info, 522 ); 352 523 } 353 524 354 525 sub user_cancel { 355 526 my Net::OpenID::Consumer $self = shift; 356 return $self-> args("openid.mode")eq "cancel";527 return $self->_message_mode eq "cancel"; 357 528 } 358 529 … … 362 533 my $post_grant = delete $opts{'post_grant'}; 363 534 Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts; 364 return $self->_fail("bad_mode") unless $self->args("openid.mode") eq "id_res"; 365 366 my $setup_url = $self->args("openid.user_setup_url"); 535 536 if ($self->_message_version == 1) { 537 return $self->_fail("bad_mode") unless $self->_message_mode eq "id_res"; 538 } 539 else { 540 return undef unless $self->_message_mode eq 'setup_needed'; 541 } 542 543 my $setup_url = $self->message("user_setup_url"); 367 544 368 545 OpenID::util::push_url_arg(\$setup_url, "openid.post_grant", $post_grant) … … 379 556 Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts; 380 557 381 return $self->_fail("bad_mode") unless $self-> args("openid.mode")eq "id_res";558 return $self->_fail("bad_mode") unless $self->_message_mode eq "id_res"; 382 559 383 560 # the asserted identity (the delegated one, if there is one, since the protocol 384 561 # knows nothing of the original URL) 385 my $a_ident = $self->args("openid.identity") or return $self->_fail("no_identity"); 386 387 my $sig64 = $self->args("openid.sig") or return $self->_fail("no_sig"); 388 my $returnto = $self->args("openid.return_to") or return $self->_fail("no_return_to"); 389 my $signed = $self->args("openid.signed"); 390 391 my $real_ident = $self->args("oic.identity") || $a_ident; 562 my $a_ident = $self->message("identity") or return $self->_fail("no_identity"); 563 564 my $sig64 = $self->message("sig") or return $self->_fail("no_sig"); 565 566 # fix sig if the OpenID auth server failed to properly escape pluses (+) in the sig 567 $sig64 =~ s/ /+/g; 568 569 my $returnto = $self->message("return_to") or return $self->_fail("no_return_to"); 570 my $signed = $self->message("signed"); 571 572 my $real_ident; 573 if ($self->_message_version == 1) { 574 $real_ident = $self->args("oic.identity") || $a_ident; 575 } 576 else { 577 $real_ident = $self->message("claimed_id") || $a_ident; 578 } 392 579 393 580 # check that returnto is for the right host … … 409 596 } 410 597 411 my $final_url; 412 my $sem_info = $self->_find_semantic_info($real_ident, \$final_url); 413 return $self->_fail("unexpected_url_redirect") unless $final_url eq $real_ident; 414 415 my $server = $sem_info->{"openid.server"} or 416 return $self->_fail("no_identity_server"); 598 my $claimed_identity = $self->claimed_identity($real_ident); 599 return $self->_fail("no_identity_server") unless $claimed_identity; 600 601 # NOTE: Currently we're expecting the "primary" OP -- that is, the one that "wins" 602 # when we do discovery -- to be the one that sends the response. Since we currently 603 # don't support falling back to other providers in the XRD case, this should always 604 # be a valid assumption unless this assersion request is unsolicited. 605 # We'll also fail if the identifier's provider priorities are twiddled between 606 # request and response, but that's unlikely enough that we're just going to ignore it. 607 608 my $final_url = $claimed_identity->claimed_url; 609 610 # OpenID 2.0 wants us to exclude the fragment part of the URL when doing equality checks 611 my $a_ident_nofragment = $a_ident; 612 my $real_ident_nofragment = $real_ident; 613 my $final_url_nofragment = $final_url; 614 if ($self->_message_version >= 2) { 615 $a_ident_nofragment =~ s/\#.*$//x; 616 $real_ident_nofragment =~ s/\#.*$//x; 617 $final_url_nofragment =~ s/\#.*$//x; 618 } 619 return $self->_fail("unexpected_url_redirect") unless $final_url_nofragment eq $real_ident_nofragment; 620 621 my $server = $claimed_identity->identity_server; 622 623 # Protocol version must match 624 return $self->_fail("protocol_version_incorrect") unless $claimed_identity->protocol_version == $self->_message_version; 417 625 418 626 # if openid.delegate was used, check that it was done correctly 419 if ($a_ident ne $real_ident) { 420 return $self->_fail("bogus_delegation") unless $sem_info->{"openid.delegate"} eq $a_ident; 421 } 422 423 my $assoc_handle = $self->args("openid.assoc_handle"); 627 if ($a_ident_nofragment ne $real_ident_nofragment) { 628 my $delegate = $claimed_identity->delegated_url; 629 my $a_ident_nofragment = $a_ident; 630 $a_ident_nofragment =~ s/\#.*$//; 631 $self->_debug("verified_identity: verifying delegate $delegate for $a_ident_nofragment"); 632 #return $self->_fail("bogus_delegation") unless $delegate eq $a_ident_nofragment; 633 if ($claimed_identity->protocol_version < 2) { 634 return $self->_fail("bogus_delegation") unless $delegate eq $a_ident; 635 } 636 } 637 638 my $assoc_handle = $self->message("assoc_handle"); 424 639 425 640 $self->_debug("verified_identity: assoc_handle: $assoc_handle"); 426 641 my $assoc = Net::OpenID::Association::handle_assoc($self, $server, $assoc_handle); 427 642 643 my %signed_fields; # key (without openid.) -> value 644 428 645 if ($assoc) { 429 646 $self->_debug("verified_identity: verifying with found association"); … … 434 651 # verify the token 435 652 my $token = ""; 436 foreach my $p (split(/,/, $signed)) { 437 $token .= "$p:" . $self->args("openid.$p") . "\n"; 653 foreach my $param (split(/,/, $signed)) { 654 my $val = $self->args("openid.$param"); 655 $token .= "$param:$val\n"; 656 $signed_fields{$param} = $val; 438 657 } 439 658 … … 452 671 ); 453 672 454 # and copy in all signed parameters that we don't already have into %post 455 foreach my $param (split(/,/, $signed)) { 456 next unless $param =~ /^\w+$/; 457 next if $post{"openid.$param"}; 458 $post{"openid.$param"} = $self->args("openid.$param"); 459 } 673 # and copy in all signed parameters that we don't already have into %post 674 foreach my $param (split(/,/, $signed)) { 675 next unless $param =~ /^[\w\.]+$/; 676 my $val = $self->args('openid.'.$param); 677 $signed_fields{$param} = $val; 678 next if $post{"openid.$param"}; 679 $post{"openid.$param"} = $val; 680 } 460 681 461 682 # if the server told us our handle as bogus, let's ask in our 462 683 # check_authentication mode whether that's true 463 if (my $ih = $self-> args("openid.invalidate_handle")) {684 if (my $ih = $self->message("invalidate_handle")) { 464 685 $post{"openid.invalidate_handle"} = $ih; 465 686 } … … 492 713 # verified! 493 714 return Net::OpenID::VerifiedIdentity->new( 494 identity => $real_ident, 495 foaf => $sem_info->{"foaf"}, 496 foafmaker => $sem_info->{"foaf.maker"}, 497 rss => $sem_info->{"rss"}, 498 atom => $sem_info->{"atom"}, 499 consumer => $self, 500 ); 715 claimed_identity => $claimed_identity, 716 consumer => $self, 717 signed_fields => \%signed_fields, 718 ); 501 719 } 502 720 … … 522 740 523 741 package OpenID::util; 742 743 use constant VERSION_1_NAMESPACE => "http://openid.net/signon/1.1"; 744 use constant VERSION_2_NAMESPACE => "http://specs.openid.net/auth/2.0"; 745 746 # I guess this is a bit daft since constants are subs anyway, 747 # but whatever. 748 sub version_1_namespace { 749 return VERSION_1_NAMESPACE; 750 } 751 sub version_2_namespace { 752 return VERSION_2_NAMESPACE; 753 } 754 sub version_1_xrds_service_url { 755 return VERSION_1_NAMESPACE; 756 } 757 sub version_2_xrds_service_url { 758 return "http://specs.openid.net/auth/2.0/signon"; 759 } 760 sub version_2_xrds_directed_service_url { 761 return "http://specs.openid.net/auth/2.0/server"; 762 } 763 sub version_2_identifier_select_url { 764 return "http://specs.openid.net/auth/2.0/identifier_select"; 765 } 524 766 525 767 # From Digest::HMAC … … 602 844 } 603 845 846 sub push_openid2_url_arg { 847 my $uref = shift; 848 my %args = @_; 849 push_url_arg($uref, 850 'openid.ns' => VERSION_2_NAMESPACE, 851 map { 852 'openid.'.$_ => $args{$_} 853 } keys %args, 854 ); 855 } 856 604 857 sub time_to_w3c { 605 858 my $time = shift || time(); … … 701 954 702 955 # so you send the user off there, and then they come back to 703 # openid-check.app, then you see what the identity server said; 704 956 # openid-check.app, then you see what the identity server said. 957 958 # Either use callback-based API (recommended)... 959 $csr->handle_server_response( 960 not_openid => sub { 961 die "Not an OpenID message"; 962 }, 963 setup_required => sub { 964 my $setup_url = shift; 965 # Redirect the user to $setup_url 966 }, 967 cancelled => sub { 968 # Do something appropriate when the user hits "cancel" at the OP 969 }, 970 verified => sub { 971 my $vident = shift; 972 # Do something with the VerifiedIdentity object $vident 973 }, 974 error => sub { 975 my $err = shift; 976 die($err); 977 }, 978 ); 979 980 # ... or handle the various cases yourself 705 981 if (my $setup_url = $csr->user_setup_url) { 706 982 # redirect/link/popup user to $setup_url … … 721 997 identity. More information is available at: 722 998 723 http:// www.danga.com/openid/999 http://openid.net/ 724 1000 725 1001 =head1 CONSTRUCTOR … … 732 1008 733 1009 You can set the C<ua>, C<cache>, C<consumer_secret>, C<required_root>, 734 and C<args> in the constructor. See the corresponding method 735 descriptions below.1010 C<minimum_version> and C<args> in the constructor. See the corresponding 1011 method descriptions below. 736 1012 737 1013 =back … … 784 1060 Your secret may not exceed 255 characters. 785 1061 1062 =item $csr->B<minimum_version>(2) 1063 1064 =item $csr->B<minimum_version> 1065 1066 Get or set the minimum OpenID protocol version supported. Currently 1067 the only useful value you can set here is 2, which will cause 1068 1.1 identifiers to fail discovery with the error C<protocol_version_incorrect>. 1069 1070 In most cases you'll want to allow both 1.1 and 2.0 identifiers, 1071 which is the default. If you want, you can set this property to 1 1072 to make this behavior explicit. 1073 1074 =item $csr->B<message>($key) 1075 1076 Obtain a value from the message contained in the request arguments 1077 with the given key. This can only be used to obtain core arguments, 1078 not extension arguments. 1079 1080 Call this method without a C<$key> argument to get a L<Net::OpenID::IndirectMessage> 1081 object representing the message. 1082 786 1083 =item $csr->B<args>($ref) 787 1084 … … 807 1104 you haven't defined a way to get at the parameters. 808 1105 1106 Most callers should instead use the C<message> method above, which 1107 abstracts away the need to understand OpenID's message serialization. 1108 809 1109 3. Get the getter: 810 1110 … … 813 1113 Without arguments, returns a subref that returns the value given a 814 1114 parameter name. 1115 1116 Most callers should instead use the C<message> method above with no 1117 arguments, which returns an object from which extension attributes 1118 can be obtained by their documented namespace URI. 815 1119 816 1120 =item $nos->B<required_root>($url_prefix) … … 848 1152 =back 849 1153 1154 =item $csr->B<handle_server_response>( %callbacks ); 1155 1156 When a request comes in that contains a response from an OpenID provider, 1157 figure out what it means and dispatch to an appropriate callback to handle 1158 the request. This is the callback-based alternative to explicitly calling 1159 the methods below in the correct sequence, and is recommended unless you 1160 need to do something strange. 1161 1162 Anything you return from the selected callback function will be returned 1163 by this method verbatim. This is useful if the caller needs to return 1164 something different in each case. 1165 1166 The available callbacks are: 1167 1168 =over 8 1169 1170 =item B<not_openid> - the request isn't an OpenID response after all. 1171 1172 =item B<setup_required>($setup_url) - the provider needs to present some UI to the user before it can respond. Send the user to the given URL by some means. 1173 1174 =item B<cancelled> - the user cancelled the authentication request from the provider's UI 1175 1176 =item B<verified>($verified_identity) - the user's identity has been successfully verified. A L<Net::OpenID::VerifiedIdentity> object is passed in. 1177 1178 =item B<error>($errcode, $errmsg) - an error has occured. An error code and message are provided. 1179 1180 =back 850 1181 851 1182 =item $csr->B<user_setup_url>( [ %opts ] ) … … 935 1266 =head1 SEE ALSO 936 1267 937 OpenID website: http://www.danga.com/openid/1268 OpenID website: http://openid.net/ 938 1269 939 1270 L<Net::OpenID::ClaimedIdentity> -- part of this module … … 946 1277 947 1278 Brad Fitzpatrick <brad@danga.com> 1279 1280 Tatsuhiko Miyagawa <miyagawa@sixapart.com> 1281 1282 Martin Atkins <mart@degeneration.co.uk> 1283 branches/release-36/extlib/Net/OpenID/VerifiedIdentity.pm
r1098 r2173 5 5 package Net::OpenID::VerifiedIdentity; 6 6 use fields ( 7 'identity', # the verified identity URL8 'id_uri', # the verified identity's URI object9 10 'foaf', # discovered foaf URL11 'foafmaker', # discovered foaf maker12 'rss', # discovered rss feed 13 'atom', # discovered atom feed14 15 'consumer', # The Net::OpenID::Consumer module which created us16 17 );7 'identity', # the verified identity URL 8 'id_uri', # the verified identity's URI object 9 10 'claimed_identity', # The ClaimedIdentity object that we've verified 11 'semantic_info', # The "semantic info" (RSS URLs, etc) at the verified identity URL 12 13 'consumer', # The Net::OpenID::Consumer module which created us 14 15 'signed_fields' , # hashref of key->value of things that were signed. without "openid." prefix 16 'signed_message', # the signed fields as an IndirectMessage object. Created when needed. 17 ); 18 18 use URI; 19 19 … … 25 25 $self->{'consumer'} = delete $opts{'consumer'}; 26 26 27 if ($self->{'identity'} = delete $opts{'identity'}) { 27 if ($self->{'claimed_identity'} = delete $opts{'claimed_identity'}) { 28 $self->{identity} = $self->{claimed_identity}->claimed_url; 28 29 unless ($self->{'id_uri'} = URI->new($self->{identity})) { 29 30 return $self-&
