root/trunk/extlib/Net/OpenID/ClaimedIdentity.pm @ 3531

Revision 3531, 11.6 kB (checked in by fumiakiy, 9 months ago)

Merged sockfish to trunk. "svn merge -r3114:3527 http://code.sixapart.com/svn/movabletype/branches/sockfish/ ."

Line 
1use strict;
2use Carp ();
3
4############################################################################
5package Net::OpenID::ClaimedIdentity;
6use 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    '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);
15
16sub new {
17    my Net::OpenID::ClaimedIdentity $self = shift;
18    $self = fields::new( $self ) unless ref $self;
19    my %opts = @_;
20    for my $f (qw( identity server consumer delegate protocol_version semantic_info )) {
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");
27    }
28
29    # lowercase the scheme and hostname
30    $self->{'identity'} =~ s!^(https?://.+?)(/(?:.*))?$!lc($1) . $2!ie;
31
32    $self->{extension_args} = {};
33
34    Carp::croak("unknown options: " . join(", ", keys %opts)) if %opts;
35    return $self;
36}
37
38sub claimed_url {
39    my Net::OpenID::ClaimedIdentity $self = shift;
40    Carp::croak("Too many parameters") if @_;
41    return $self->{'identity'};
42}
43
44sub delegated_url {
45    my Net::OpenID::ClaimedIdentity $self = shift;
46    Carp::croak("Too many parameters") if @_;
47    return $self->{'delegate'};
48}
49
50sub identity_server {
51    my Net::OpenID::ClaimedIdentity $self = shift;
52    Carp::croak("Too many parameters") if @_;
53    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;
82}
83
84sub check_url {
85    my Net::OpenID::ClaimedIdentity $self = shift;
86    my (%opts) = @_;
87
88    my $return_to   = delete $opts{'return_to'};
89    my $trust_root  = delete $opts{'trust_root'};
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'};
94
95    Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts;
96    Carp::croak("Invalid/missing return_to") unless $return_to =~ m!^https?://!;
97
98    my $csr = $self->{consumer};
99
100    my $ident_server = $self->{server} or
101        Carp::croak("No identity server");
102
103    # get an assoc (or undef for dumb mode)
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    }
117
118    my $identity_arg = $self->{'delegate'} || $self->{'identity'};
119
120    # make a note back to ourselves that we're using a 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) {
123        OpenID::util::push_url_arg(\$return_to,
124                                   "oic.identity",  $self->{identity});
125    }
126
127    # add a HMAC-signed time so we can verify the return_to URL wasn't spoofed
128    my $sig_time = time();
129    my $c_secret = $csr->_get_consumer_secret($sig_time);
130    my $sig = substr(OpenID::util::hmac_sha1_hex($sig_time, $c_secret), 0, 20);
131    OpenID::util::push_url_arg(\$return_to,
132                               "oic.time", "${sig_time}-$sig");
133
134    my $curl = $ident_server;
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;
192
193    $self->{consumer}->_debug("check_url for (del=$self->{delegate}, id=$self->{identity}) = $curl");
194    return $curl;
195}
196
197
1981;
199
200__END__
201
202=head1 NAME
203
204Net::OpenID::ClaimedIdentity - a not-yet-verified OpenID identity
205
206=head1 SYNOPSIS
207
208  use Net::OpenID::Consumer;
209  my $csr = Net::OpenID::Consumer->new;
210  ....
211  my $cident = $csr->claimed_identity("bradfitz.com")
212    or die $csr->err;
213
214  if ($AJAX_mode) {
215    my $url = $cident->claimed_url;
216    my $openid_server = $cident->identity_server;
217    # ... return JSON with those to user agent (whose request was
218    # XMLHttpRequest, probably)
219  }
220
221  if ($CLASSIC_mode) {
222    my $check_url = $cident->check_url(
223      delayed_return => 1,
224      return_to      => "http://example.com/get-identity.app",
225      trust_root     => "http://*.example.com/",
226    );
227    WebApp::redirect($check_url);
228  }
229
230=head1 DESCRIPTION
231
232After L<Net::OpenID::Consumer> crawls a user's declared identity URL
233and finds openid.server link tags in the HTML head, you get this
234object.  It represents an identity that can be verified with OpenID
235(the link tags are present), but hasn't been actually verified yet.
236
237=head1 METHODS
238
239=over 4
240
241=item $url = $cident->B<claimed_url>
242
243The URL, now canonicalized, that the user claims to own.  You can't
244know whether or not they do own it yet until you send them off to the
245check_url, though.
246
247=item $id_server = $cident->B<identity_server>
248
249Returns the identity server that will assert whether or not this
250claimed identity is valid, and sign a message saying so.
251
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
284=item $url = $cident->B<check_url>( %opts )
285
286Makes the URL that you have to somehow send the user to in order to
287validate their identity.  The options to put in %opts are:
288
289=over
290
291=item C<return_to>
292
293The URL that the identity server should redirect the user with either
294a verified identity signature -or- a user_setup_url (if the assertion
295couldn't be made).  This URL may contain query parameters, and the
296identity server must preserve them.
297
298=item C<trust_root>
299
300The URL that you want the user to actually see and declare trust for.
301Your C<return_to> URL must be at or below your trust_root.  Sending
302the trust_root is optional, and defaults to your C<return_to> value,
303but it's highly recommended (and prettier for users) to see a simple
304trust_root.  Note that the trust root may contain a wildcard at the
305beginning of the host, like C<http://*.example.com/>
306
307=item C<delayed_return>
308
309If set to a true value, the check_url returned will indicate to the
310user's identity server that it has permission to control the user's
311user-agent for awhile, giving them real pages (not just redirects) and
312lets them bounce around the identity server site for awhile until
313the requested assertion can be made, and they can finally be redirected
314back to your return_to URL above.
315
316The default value, false, means that the identity server will
317immediately return to your return_to URL with either a "yes" or "no"
318answer.  In the "no" case, you'll instead have control of what to do,
319and you'll be sent the identity server's user_setup_url where you'll
320have to somehow send the user (be it link, redirect, or pop-up
321window).
322
323When writing a dynamic "AJAX"-style application, you can't use
324delayed_return because the remote site can't usefully take control of
325a 1x1 pixel hidden IFRAME, so you'll need to get the user_setup_url
326and present it to the user somehow.
327
328=back
329
330=back
331
332=head1 COPYRIGHT, WARRANTY, AUTHOR
333
334See L<Net::OpenID::Consumer> for author, copyrignt and licensing information.
335
336=head1 SEE ALSO
337
338L<Net::OpenID::Consumer>
339
340L<Net::OpenID::VerifiedIdentity>
341
342L<Net::OpenID::Server>
343
344Website:  L<http://www.openid.net/>
345
Note: See TracBrowser for help on using the browser.