Changeset 2173

Show
Ignore:
Timestamp:
04/30/08 23:58:44 (7 months ago)
Author:
fumiakiy
Message:

Checking in Net::OpenID::Consumer for OpenID 2.0 support in commenting to Movable Type powered blog. Now commenters can login to your blog by entering "yahoo.com" or "myopenid.com" as their OpenID URL in the sign in screen. BugId:69889

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/release-36/extlib/Net/OpenID/Association.pm

    r1098 r2173  
    3838} 
    3939 
     40sub type { 
     41    my $self = shift; 
     42    die if @_; 
     43    $self->{'type'}; 
     44} 
     45 
    4046sub server { 
    4147    my Net::OpenID::Association $self = shift; 
     
    6470# a new assoc_handle if none is found, or has expired 
    6571sub 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; 
    6776 
    6877    # closure to return undef (dumb consumer mode) and log why 
     
    7584    return $dumb->("no_cache") unless $cache; 
    7685 
    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            } 
    8495        } 
    8596    } 
     
    94105                "openid.dh_consumer_public" => OpenID::util::bi2arg($dh->pub_key), 
    95106                ); 
     107 
     108    if ($protocol_version == 2) { 
     109        $post{"openid.ns"} = OpenID::util::version_2_namespace(); 
     110    } 
    96111 
    97112    my $req = HTTP::Request->new(POST => $server); 
     
    160175    $cache->set("shandle:$server", $ahandle); 
    161176 
     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 
    162183    return $assoc; 
    163184} 
  • branches/release-36/extlib/Net/OpenID/ClaimedIdentity.pm

    r1098 r2173  
    55package Net::OpenID::ClaimedIdentity; 
    66use 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); 
    1215 
    1316sub new { 
     
    1518    $self = fields::new( $self ) unless ref $self; 
    1619    my %opts = @_; 
    17     for my $f (qw( identity server consumer delegate )) { 
     20    for my $f (qw( identity server consumer delegate protocol_version semantic_info )) { 
    1821        $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"); 
    1927    } 
    2028 
     
    2230    $self->{'identity'} =~ s!^(https?://.+?)(/(?:.*))?$!lc($1) . $2!ie; 
    2331 
     32    $self->{extension_args} = {}; 
     33 
    2434    Carp::croak("unknown options: " . join(", ", keys %opts)) if %opts; 
    2535    return $self; 
     
    3242} 
    3343 
     44sub delegated_url { 
     45    my Net::OpenID::ClaimedIdentity $self = shift; 
     46    Carp::croak("Too many parameters") if @_; 
     47    return $self->{'delegate'}; 
     48} 
     49 
    3450sub identity_server { 
    3551    my Net::OpenID::ClaimedIdentity $self = shift; 
    3652    Carp::croak("Too many parameters") if @_; 
    3753    return $self->{server}; 
     54} 
     55 
     56sub protocol_version { 
     57    my Net::OpenID::ClaimedIdentity $self = shift; 
     58    Carp::croak("Too many parameters") if @_; 
     59    return $self->{protocol_version}; 
     60} 
     61 
     62sub 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 
     73sub 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; 
    3882} 
    3983 
     
    4589    my $trust_root  = delete $opts{'trust_root'}; 
    4690    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'}; 
    4794 
    4895    Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts; 
     
    55102 
    56103    # 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    } 
    58117 
    59118    my $identity_arg = $self->{'delegate'} || $self->{'identity'}; 
    60119 
    61120    # 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) { 
    63123        OpenID::util::push_url_arg(\$return_to, 
    64124                                   "oic.identity",  $self->{identity}); 
     
    73133 
    74134    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; 
    86192 
    87193    $self->{consumer}->_debug("check_url for (del=$self->{delegate}, id=$self->{identity}) = $curl"); 
     
    144250claimed identity is valid, and sign a message saying so. 
    145251 
     252=item $url = $cident->B<delegated_url> 
     253 
     254If the claimed URL is using delegation, this returns the delegated identity that will 
     255actually be sent to the identity server. 
     256 
     257=item $version = $cident->B<protocol_version> 
     258 
     259Determines whether this identifier is to be verified by OpenID 1.1 
     260or by OpenID 2.0. Returns C<1> or C<2> respectively. This will 
     261affect the way the C<check_url> is constructed. 
     262 
     263=item $cident->B<set_extension_args>($ns_uri, $args) 
     264 
     265If 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. 
     267For 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 
     278Note that when making an OpenID 1.1 request, only the Simple Registration 
     279extension is supported. There was no general extension mechanism defined 
     280in OpenID 1.1, so SREG (with the namespace URI as in the example above) 
     281is supported as a special case. All other extension namespaces will 
     282be silently ignored when making a 1.1 request. 
     283 
    146284=item $url = $cident->B<check_url>( %opts ) 
    147285 
     
    204342L<Net::OpenID::Server> 
    205343 
    206 Website:  L<http://www.danga.com/openid/> 
    207  
     344Website:  L<http://www.openid.net/> 
     345 
  • branches/release-36/extlib/Net/OpenID/Consumer.pm

    r1098 r2173  
    1010 
    1111use vars qw($VERSION); 
    12 $VERSION = "0.12"; 
     12$VERSION = "0.14"; 
    1313 
    1414use 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); 
    2426 
    2527use Net::OpenID::ClaimedIdentity; 
    2628use Net::OpenID::VerifiedIdentity; 
    2729use Net::OpenID::Association; 
     30use Net::OpenID::Yadis; 
     31use Net::OpenID::IndirectMessage; 
    2832 
    2933use MIME::Base64 (); 
     
    3842    my %opts = @_; 
    3943 
     44    $opts{minimum_version} ||= 1; 
     45 
    4046    $self->{ua}            = delete $opts{ua}; 
    4147    $self->args            ( delete $opts{args}            ); 
     
    4349    $self->consumer_secret ( delete $opts{consumer_secret} ); 
    4450    $self->required_root   ( delete $opts{required_root}   ); 
     51    $self->minimum_version ( delete $opts{minimum_version} ); 
    4552 
    4653    $self->{debug} = delete $opts{debug}; 
     
    4855    Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts; 
    4956    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? 
     64sub disable_version_1 { 
     65    my $self = shift; 
     66    $self->{minimum_version} = 2.0; 
    5067} 
    5168 
     
    5370sub consumer_secret { &_getset; } 
    5471sub required_root   { &_getset; } 
     72sub minimum_version { &_getset; } 
    5573 
    5674sub _getset { 
     
    98116        } elsif (ref $what eq "HASH") { 
    99117            $getter = sub { $what->{$_[0]}; }; 
    100         } elsif (ref $what eq "CGI") { 
     118        } elsif (UNIVERSAL::isa($what, "CGI")) { 
    101119            $getter = sub { scalar $what->param($_[0]); }; 
    102120        } elsif (ref $what eq "Apache") { 
     
    112130        if ($getter) { 
    113131            $self->{args} = $getter; 
     132            $self->{message} = Net::OpenID::IndirectMessage->new($what, minimum_version => $self->minimum_version); 
    114133        } 
    115134    } 
    116135    $self->{args}; 
     136} 
     137 
     138sub 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 
     148sub _message_mode { 
     149    my $message = $_[0]->message; 
     150    return $message ? $message->mode : undef; 
     151} 
     152 
     153sub _message_version { 
     154    my $message = $_[0]->message; 
     155    return $message ? $message->protocol_version : undef; 
    117156} 
    118157 
     
    141180        'no_head_tag' => "URL provided doesn't seem to have a head tag.", 
    142181        '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", 
    143186    }->{$code}; 
    144187 
     
    239282            $val =~ m!\bhref=[\"\']([^\"\']+)[\"\']!i) { 
    240283            $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; 
    241293            next; 
    242294        } 
     
    302354    } 
    303355 
    304     $self->_debug("semantic info ($url) = " . join(", ", %$ret))
     356    $self->_debug("semantic info ($url) = " . join(", ", map { $_.' => '.$ret->{$_} } keys %$ret)) if $self->{debug}
    305357 
    306358    return $ret; 
     
    317369    return $self->_fail("no_identity_server") unless $sem_info->{"openid.server"}; 
    318370    $sem_info->{"openid.server"}; 
     371} 
     372 
     373sub is_server_response { 
     374    my Net::OpenID::Consumer $self = shift; 
     375    return $self->_message_mode ? 1 : 0; 
     376} 
     377 
     378sub 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 
    319405} 
    320406 
     
    334420    return $self->_fail("bogus_url", "Invalid URL") unless $url =~ m!^https?://!i; 
    335421    # add a slash, if none exists 
    336     $url .= "/" unless $url =~ m!^http://.+/!i; 
     422    $url .= "/" unless $url =~ m!^https?://.+/!i; 
    337423 
    338424    my $final_url; 
    339425 
    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; 
    345514 
    346515    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    ); 
    352523} 
    353524 
    354525sub user_cancel { 
    355526    my Net::OpenID::Consumer $self = shift; 
    356     return $self->args("openid.mode") eq "cancel"; 
     527    return $self->_message_mode eq "cancel"; 
    357528} 
    358529 
     
    362533    my $post_grant = delete $opts{'post_grant'}; 
    363534    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"); 
    367544 
    368545    OpenID::util::push_url_arg(\$setup_url, "openid.post_grant", $post_grant) 
     
    379556    Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts; 
    380557 
    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"; 
    382559 
    383560    # the asserted identity (the delegated one, if there is one, since the protocol 
    384561    # 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    } 
    392579 
    393580    # check that returnto is for the right host 
     
    409596    } 
    410597 
    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; 
    417625 
    418626    # 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"); 
    424639 
    425640    $self->_debug("verified_identity: assoc_handle: $assoc_handle"); 
    426641    my $assoc = Net::OpenID::Association::handle_assoc($self, $server, $assoc_handle); 
    427642 
     643    my %signed_fields;   # key (without openid.) -> value 
     644 
    428645    if ($assoc) { 
    429646        $self->_debug("verified_identity: verifying with found association"); 
     
    434651        # verify the token 
    435652        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; 
    438657        } 
    439658 
     
    452671                    ); 
    453672 
    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        } 
    460681 
    461682        # if the server told us our handle as bogus, let's ask in our 
    462683        # check_authentication mode whether that's true 
    463         if (my $ih = $self->args("openid.invalidate_handle")) { 
     684        if (my $ih = $self->message("invalidate_handle")) { 
    464685            $post{"openid.invalidate_handle"} = $ih; 
    465686        } 
     
    492713    # verified! 
    493714    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    ); 
    501719} 
    502720 
     
    522740 
    523741package OpenID::util; 
     742 
     743use constant VERSION_1_NAMESPACE => "http://openid.net/signon/1.1"; 
     744use 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. 
     748sub version_1_namespace { 
     749    return VERSION_1_NAMESPACE; 
     750} 
     751sub version_2_namespace { 
     752    return VERSION_2_NAMESPACE; 
     753} 
     754sub version_1_xrds_service_url { 
     755    return VERSION_1_NAMESPACE; 
     756} 
     757sub version_2_xrds_service_url { 
     758    return "http://specs.openid.net/auth/2.0/signon"; 
     759} 
     760sub version_2_xrds_directed_service_url { 
     761    return "http://specs.openid.net/auth/2.0/server"; 
     762} 
     763sub version_2_identifier_select_url { 
     764    return "http://specs.openid.net/auth/2.0/identifier_select"; 
     765} 
    524766 
    525767# From Digest::HMAC 
     
    602844} 
    603845 
     846sub 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 
    604857sub time_to_w3c { 
    605858    my $time = shift || time(); 
     
    701954 
    702955  # 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 
    705981  if (my $setup_url = $csr->user_setup_url) { 
    706982       # redirect/link/popup user to $setup_url 
     
    721997identity.  More information is available at: 
    722998 
    723   http://www.danga.com/openid
     999  http://openid.net
    7241000 
    7251001=head1 CONSTRUCTOR 
     
    7321008 
    7331009You 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. 
     1010C<minimum_version> and C<args> in the constructor.  See the corresponding 
     1011method descriptions below. 
    7361012 
    7371013=back 
     
    7841060Your secret may not exceed 255 characters. 
    7851061 
     1062=item $csr->B<minimum_version>(2) 
     1063 
     1064=item $csr->B<minimum_version> 
     1065 
     1066Get or set the minimum OpenID protocol version supported. Currently 
     1067the only useful value you can set here is 2, which will cause 
     10681.1 identifiers to fail discovery with the error C<protocol_version_incorrect>. 
     1069 
     1070In most cases you'll want to allow both 1.1 and 2.0 identifiers, 
     1071which is the default. If you want, you can set this property to 1 
     1072to make this behavior explicit. 
     1073 
     1074=item $csr->B<message>($key) 
     1075 
     1076Obtain a value from the message contained in the request arguments 
     1077with the given key. This can only be used to obtain core arguments, 
     1078not extension arguments. 
     1079 
     1080Call this method without a C<$key> argument to get a L<Net::OpenID::IndirectMessage> 
     1081object representing the message. 
     1082 
    7861083=item $csr->B<args>($ref) 
    7871084 
     
    8071104you haven't defined a way to get at the parameters. 
    8081105 
     1106Most callers should instead use the C<message> method above, which 
     1107abstracts away the need to understand OpenID's message serialization. 
     1108 
    80911093. Get the getter: 
    8101110 
     
    8131113Without arguments, returns a subref that returns the value given a 
    8141114parameter name. 
     1115 
     1116Most callers should instead use the C<message> method above with no 
     1117arguments, which returns an object from which extension attributes 
     1118can be obtained by their documented namespace URI. 
    8151119 
    8161120=item $nos->B<required_root>($url_prefix) 
     
    8481152=back 
    8491153 
     1154=item $csr->B<handle_server_response>( %callbacks ); 
     1155 
     1156When a request comes in that contains a response from an OpenID provider, 
     1157figure out what it means and dispatch to an appropriate callback to handle 
     1158the request. This is the callback-based alternative to explicitly calling 
     1159the methods below in the correct sequence, and is recommended unless you 
     1160need to do something strange. 
     1161 
     1162Anything you return from the selected callback function will be returned 
     1163by this method verbatim. This is useful if the caller needs to return 
     1164something different in each case. 
     1165 
     1166The 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 
    8501181 
    8511182=item $csr->B<user_setup_url>( [ %opts ] ) 
     
    9351266=head1 SEE ALSO 
    9361267 
    937 OpenID website: http://www.danga.com/openid
     1268OpenID website: http://openid.net
    9381269 
    9391270L<Net::OpenID::ClaimedIdentity> -- part of this module 
     
    9461277 
    9471278Brad Fitzpatrick <brad@danga.com> 
     1279 
     1280Tatsuhiko Miyagawa <miyagawa@sixapart.com> 
     1281 
     1282Martin Atkins <mart@degeneration.co.uk> 
     1283 
  • branches/release-36/extlib/Net/OpenID/VerifiedIdentity.pm

    r1098 r2173  
    55package Net::OpenID::VerifiedIdentity; 
    66use fields ( 
    7             'identity',  # the verified identity URL 
    8             'id_uri',  # the verified identity's URI object 
    9  
    10             'foaf',      # discovered foaf URL 
    11             'foafmaker', # discovered foaf maker 
    12             'rss',       # discovered rss feed 
    13             'atom',      # discovered atom feed 
    14  
    15             'consumer',  # The Net::OpenID::Consumer module which created us 
    16  
    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); 
    1818use URI; 
    1919 
     
    2525    $self->{'consumer'} = delete $opts{'consumer'}; 
    2626 
    27     if ($self->{'identity'} = delete $opts{'identity'}) { 
     27    if ($self->{'claimed_identity'} = delete $opts{'claimed_identity'}) { 
     28        $self->{identity} = $self->{claimed_identity}->claimed_url; 
    2829        unless ($self->{'id_uri'} = URI->new($self->{identity})) { 
    2930            return $self-&