OpenID authentication to Google Apps via Perl and

2019-04-01 23:17发布

问题:

I asked this over on Google's support forums for Apps integration, but got zero response. Maybe somebody here can help steer me in the right direction.

I'm trying to integrate a Perl application with Google Apps, and I'm having some trouble with the OpenID authentication. I've been using this PHP tutorial from Google as a kind of reference, since there are no Perl examples I can find.

My initial file, index.cgi (referred by manifest.xml, and the starting point of the OpenID transaction) is as follows:

use Net::OpenID::Consumer;
use CGI;
# ...

my $q = CGI->new();

my $domain = $q->param('domain');
if (!$domain) {
    print $q->header(), 'Provide domain please.';
    exit 0;
}
# my website
my $root = 'http://www.example.com/';

my $csr = Net::OpenID::Consumer->new(
     # The user agent which sends the openid off to the server
     ua => LWP::UserAgent->new,
     # Who we are
     required_root => $root,
     # Consumer Key Secret from Google Apps Marketplace
     consumer_secret => 'Zzzzzz9zzAAAAA....'
);

my $claimed_id = $csr->claimed_identity(
    'https://www.google.com/accounts/o8/site-xrds?hd=' . $domain);

if ($claimed_id) {
    my $check_url = $claimed_id->check_url(
        # Upon validation, the user will be returned here, and real
        # work may begin
        return_to => $root . '/return.cgi',
        trust_root => $root
    );
    print $q->redirect($check_url);
}
else {
    print $q->header(), "Error";
}

This part seems to be working. That is, I get redirected to return.cgi with a bunch of openid.* parameters. However, at this point I get the following error:

no_identity_server The provided URL doesn't declare its OpenID identity server

I'm using the latest version of the Net::OpenID::Consumer module.

Here are the significant bits of return.cgi:

my $q = CGI->new();
my $csr = Net::OpenID::Consumer->new(
     ua => LWP::UserAgent->new,
     # The root of our URL
     required_root => 'http://www.example.com/',
     # Our password.
     consumer_secret => 'Zzzzzz9zzAAAAA....',
     # Where to get the information from.
     args  => $q
);

print $q->header();
$csr->handle_server_response(
     not_openid => sub {
         print "That's not an OpenID message. Did you just type in the URL?";
     },
     setup_required => sub {
         my $setup_url = shift;
         print 'You need to do something <a href="#">here</a>.';
     },
     cancelled => sub {
         print 'You cancelled your login.';
     },
     verified => sub {
         my $vident = shift;
         my $url = $vident->url;
         print "You are verified as '$url'. ** FIN **";
     },
     error => sub { die "Can't figure it out: ", @_; }
);

As you can imagine, I'm wanting the verified sub to fire, but instead I'm getting an error. Anything obvious I'm missing? Any help would be appreciated.

回答1:

So the solution, it turns out, is to switch modules. I changed to the skimpily documented Net::Google::FederatedLogin, and things are now working. The code is as follows (substitute example.com below for your actual developer's domain).

In your Google Apps Marketplace vendor profile, add the URL to index.cgi in the Application Manifest:

...
<Url>http://www.example.com/index.cgi?from=google&amp;domain=${DOMAIN_NAME}</Url>
...

Then add the following code to your servers.

index.cgi

use CGI;
use Net::Google::FederatedLogin;

my $q = CGI->new();

my $domain = $q->param('domain');
if (!$domain) {
    print $q->header(), 'Provide domain please.';
    exit 0;
}

my $fl = Net::Google::FederatedLogin->new(
    claimed_id => 
        'https://www.google.com/accounts/o8/site-xrds?hd=' . $domain,
    return_to =>
        'http://www.example.com/return.cgi',
    extensions => [
        {
            ns          => 'ax',
            uri         => 'http://openid.net/srv/ax/1.0',
            attributes  => {
                mode        => 'fetch_request',
                required    => 'email',
                type        => {
                    email => 'http://axschema.org/contact/email'
                }
            }
        }
    ] );

print $q->redirect($fl->get_auth_url());

return.cgi

use CGI;
use Net::Google::FederatedLogin;

my $q = CGI->new();
print $q->header();

my $fl = Net::Google::FederatedLogin->new(  
    cgi => $q,
    return_to =>
        'http://www.example.com/return.cgi' );

eval { $fl->verify_auth(); };
if ($@) {
    print 'Error: ' . $@;
}
else {
    # we've authenticated and gotten attributes --
    my $ext = $fl->get_extension('http://openid.net/srv/ax/1.0');
    print $ext->get_parameter('value.email'); 
}

(For a full sample, plus OAuth access to user data, see this post on my blog.)



回答2:

In some cases, reportedly, this is caused by Perl missing Net::SSL, making it fail on Google's SSL URLs.



回答3:

The step where it's failing is where it performs discovery on the identifier asserted by Google's server. For some reason discovery on that identifier URL is failing. It would be helpful to know what OpenID identifier the Google server is asserting to try to debug why discovery is not working for it.



回答4:

I'm not sure what $domain is there, but you may need to escape it:

use URI::Escape 'uri_escape';
....
my $claimed_id = $csr->claimed_identity(
    'https://www.google.com/accounts/o8/site-xrds?hd=' . uri_escape($domain) );

Also, the consumer_secret used by Net::OpenID::Consumer has no relationship to any other secret.