Google-OAuth

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

	- use JSON::from_json instead of JSON::Parse
	- CGI::Simple fails in mod_perl
	- Added scopes{drive}
	- Added optional argument to Google::OAuth::Request methods
	- Added Google::OAuth::Headers for customized headers

0.04  Mon May 20 14:50:18 EDT 2013
	- Added provisions for Facebook OAuth to Install.pm
	- Added classID() method returns constant integer
	- Added grant_type() method returns constant string
	- scope() return always includes 'calendar.readonly' value
	- Some method calls are now explicitly called using a package
	- Added sample Facebook::OAuth

0.05  Sat Jun  8 19:43:38 EDT 2013
	- Documentation changes
	- Added LWP::Protocol::https prerequisite

lib/Google/OAuth.pm  view on Meta::CPAN

	my $method = shift ;

	return Google::OAuth::Request::headers( $method ),
			Authorization =>
			  join ' ', @$self{ qw( token_type access_token ) } ;
	}

sub emailkey {
	my $self = shift ;
	my $url = 'https://www.googleapis.com'
				.'/calendar/v3/users/me/calendarList' ;
	my $calinfo = $self->content( GET => $url ) ;
	my @owner = grep $_->{accessRole} eq 'owner', @{ $calinfo->{items} } ;
	return $self->{emailkey} = $owner[0]->{summary} ;
	}


package Google::OAuth::Client ;

require Exporter;

lib/Google/OAuth.pm  view on Meta::CPAN

	return undef ;
	}

sub dsn {
	return $client{dsn} ;
	}

my %scopes = (
	'm8.feeds' 
			=> 'https://www.google.com/m8/feeds',
	'calendar' 
			=> 'https://www.googleapis.com/auth/calendar',
	'calendar.readonly' 
			=> 'https://www.googleapis.com/auth/calendar.readonly',
	'drive.readonly' 
			=> 'https://www.googleapis.com/auth/drive.readonly',
	'drive' 
			=> 'https://www.googleapis.com/auth/drive',
        ) ;

sub new {
	my $package = shift ;
	my $self = {} ;
	$self->{args} = $package->queryargs( @_ ) if @_ ;
	return bless $self, $package ;
	}

sub scope {
	shift @_ if $_[0] eq __PACKAGE__ ;
	my $self = ref $_[0] eq __PACKAGE__? shift @_: undef ;
	my %args = map { $_ => 1 } ( @_, 'calendar.readonly' ) ;

	my $scope = join ' ', map { $scopes{$_} } keys %args ;
	return $scope unless $self ;

	$self->{scope} = $scope ;
	return $self ;
	}

sub queryargs {
	my $package = shift ;

lib/Google/OAuth.pm  view on Meta::CPAN

  use Google::OAuth ;

  ## show a list of users 
  print join "\n", @users = Google::OAuth->token_list, '' ;

  ## eg $users[0] = 'perlmonster@gmail.com'
  $token = Google::OAuth->token( $users[0] ) ;

  ## Get Google Calendar data for this user
  $calobject = $token->content( GET => 'https://www.googleapis.com'
		.'/calendar/v3/users/me/calendarList' 
		) ;

  ## Insert a calendar event
  %event = ( ... ) ;  ## See Google documentation
  $url = sprintf 'https://www.googleapis.com'
		.'/calendar/v3/calendars/%s/events', $token->{emailkey} ;
  $token->content( POST => $url, 
		Google::OAuth::CGI->new( \%event )->query_string ) ;


=head1 DESCRIPTION

Google::OAuth provides the capability to utilize the Google App's published 
API.  The link below (to Google's Calendar reference) demonstrates their
API in the form of HTTP REST requests.  This API is consistent with the 
arguments of a Google::OAuth token's methods.

  https://developers.google.com/google-apps/calendar/v3/reference/

Based on the documentation, the integration process looks deceptively easy.  
Google::OAuth takes the same approach by providing a framework that also 
looks deceptively easy.  Notwithstanding, this package includes the tools 
to get you set up and running.


=head1 BACKGROUND

The complete installation will probably take several hours.  If you're

lib/Google/OAuth.pm  view on Meta::CPAN

already been tested and used.  But it's important to test your entire
integration process using Google's API and the methods described in the
L<SYNOPSIS> section.  It'll be practically impossible to test or troubleshoot
on other users' accounts.


=head1 CREATING TOKENS

Most likely, your API tests will fail.  By default Google's tokens do not 
allow data access without explicitly defining scope. The token created during
installation is only capable of reading your calendar data.  For additional 
access, you'll need to generate a new token.

As described above, this process consists of 3 phases:

=over 8

=item 1. Generate a request

=item 2. Acquire Google's ticket, the I<Grant Code>

lib/Google/OAuth.pm  view on Meta::CPAN

request available to the user; and the user needs to trust you enough to 
approve the request.  The request is not user specific, the same request
can be used by anyone.  Nevertheless, given the personal nature, it's 
probably best to email your request.  Naturally gaining trust is trickier.

Most of this package's methods operate on C<Google::OAuth> tokens.  Since
these requests are more generic, and independent of any token, the token
request method uses a more general superclass, C<Google::OAuth::Client>.

  print Google::OAuth::Client->new->scope(
    		'calendar.readonly' )->token_request ;

The token_request method is detailed below.  There are numerous internal 
definitions described within the method.  Based on my experience, these 
default values are sufficient.  However, the default values can be 
overridden using arguments passed to the constructor.

On the other hand, the list elements passed as arguments to the C<scope()> 
method must be explicit.  Each element must be a value from the list below.
This list hasn't been verified for completeness, and more options may be added 
in the next revision:

=over 8

=item I<m8.feeds>

=item I<calendar>

=item I<calendar.readonly>

=item I<drive.readonly>

=item I<drive>

=back 8

The C<token_request()> method output is a legal, functional URL.  The 
caller is responsible for wrapping the HTML.

lib/Google/OAuth.pm  view on Meta::CPAN

examining the method's result, which should contain an I<emailkey> value. 
When Google is being overly fussy, the result will look like:

  ## The 'requested' value is not part of Google's response
  $token = {
          'requested' => 1366389672,
          'error' => 'Invalid Grant'
        };

Another problem may occur if the token has insufficient privileges to 
access calendar data:

  $token = bless( {
                 'refresh_token' => '1/1v3Tvzj31e5M',
                 'expires_in' => '3600',
                 'requested' => 1366390047,
                 'access_token' => 'ya29.AHES6ZS',
                 'token_type' => 'Bearer'
               }, 'Google::OAuth' );

In this case, Google issued a valid token, but C<grant_code()> was not able to

lib/Google/OAuth.pm  view on Meta::CPAN


  ## eg $users[0] = 'perlmonster@gmail.com'
  $token = Google::OAuth->token( $users[0] ) ;

  ## Intervening operations of an hour or longer

  $token = $token->token if $token->expired ;

  ## Get Google Calendar data for this users
  $calobject = $token->content( GET => 'https://www.googleapis.com'
		.'/calendar/v3/users/me/calendarList' ) ;

Persistent objects in L<NoSQL::PL2SQL> can be either volatile or non-volatile.
Volatile objects cache write operations until the object is destroyed, and 
therefore must be explicitly destroyed.  As mentioned above, the 
C<grant_code()> method returns a volatile object.  As a convenience, the
C<token()> method returns non-volatile objects.  This adds a small
inefficency of a database write on every subsequent C<token()> method call.
Given how infrequently that method must be called, this is not a significant
concern.  Here is the volatile object approach nonetheless:

lib/Google/OAuth.pm  view on Meta::CPAN


  ## eg $users[0] = 'perlmonster@gmail.com'
  $token = Google::OAuth->SQLObject( $users[0] )->token ;

  ## Intervening operations of an hour or longer

  $token->token if $token->expired ;

  ## Get Google Calendar data for this users
  $calobject = $token->content( GET => 'https://www.googleapis.com'.
		'/calendar/v3/users/me/calendarList' ) ;

  ## Somewhere before exiting
  undef $token ;


=head1 METHOD SUMMARY

=head2 Google::OAuth

C<Google::OAuth> subclasses the following:

lib/Google/OAuth.pm  view on Meta::CPAN

=head1 EXPORT

none

=head1 SEE ALSO

=over 8

=item  http://developers.google.com/

=item  http://www.tqis.com/eloquency/googlecalendar.htm

=back 8

There is a page on my developer site to discuss Google::OAuth

=over 8

=item  http://pl2sql.tqis.com/pl2sql/GoogleOAuth/

=back 8

lib/Google/OAuth/Install.pm  view on Meta::CPAN


	return 0 ;
	}

sub grantcode {
	return unless init('grantcode') ;
	return if settings(1) ;

	Google::OAuth->setclient ;
	my $link = Google::OAuth::Client->new->scope(
			'calendar.readonly' )->token_request ;
	printf '<a href="%s">Get Grant Code</a>%s', $link, "\n" ;
	}

sub test {
	init('test') unless @_ ;
	return if settings(1) ;

	my $code = $Google::OAuth::Config::test{grantcode} ;
	return print STDERR <<'eof' unless $code ;
Generate a URL to acquire Grant Code from Google

samples/google.pm  view on Meta::CPAN

	}

sub url {
	my $package = shift ;
	my $token = shift ;
	my @args = @_ ;
	my @parms = () ;
	push @parms, [ singleEvents => 'true' ] ;
	push @parms, [ orderBy => 'startTime' ] ;

	## for bucc calendar
	push @parms, [ timeMin => &sunday ] ;
	
	push @parms, [ splice @args, 0, 2 ] while @args ;

	my $url = join '/', 'https://www.googleapis.com',
			'calendar/v3/calendars',
			Google::OAuth::CGI->encode( $token->{emailkey} ), 
			'events' ;
	return $url unless @parms ;

	return join '?', $url, join '&', map { join '=', 
			$_->[0] => Google::OAuth::CGI->encode( $_->[1] ) 
			} @parms ;
	} 

sub sunday {

t/Google-OAuth.t  view on Meta::CPAN

# Insert your test code below, the Test::More module is use()ed here so read
# its man page ( perldoc Test::More ) for help writing this test script.

sub linkcompare {
	my @links = map { join '', sort split //, $_ } @_ ;
	return $links[0] eq $links[1] ;
	}

my @test ;
push @test, split /\n/, <<'eof' ;
https://accounts.google.com/o/oauth2/auth?response_type=code&approval_prompt=force&redirect_uri=XFygUanB0BYszi3ehzNxfJM5BBV6xkSm7CcKmEAo&client_id=Js6XzxwxR9KA0g0kkEdEFjPPyv9kNKLfmfUuhu3A&access_type=offline&scope=https%3A%2F%2Fwww.googleapis.com%2Fa...
https://accounts.google.com/o/oauth2/auth?response_type=code&approval_prompt=force&redirect_uri=XFygUanB0BYszi3ehzNxfJM5BBV6xkSm7CcKmEAo&client_id=Js6XzxwxR9KA0g0kkEdEFjPPyv9kNKLfmfUuhu3A&access_type=offline&scope=https%3A%2F%2Fwww.google.com%2Fm8%2F...
https://accounts.google.com/o/oauth2/auth?response_type=code&approval_prompt=force&redirect_uri=XFygUanB0BYszi3ehzNxfJM5BBV6xkSm7CcKmEAo&client_id=FTNmkFXfh6OZH5jXsW7qLe3bgsnl7ZObPfsuscNy&access_type=offline&scope=https%3A%2F%2Fwww.googleapis.com%2Fa...
https://accounts.google.com/o/oauth2/auth?client_id=FTNmkFXfh6OZH5jXsW7qLe3bgsnl7ZObPfsuscNy&foo=client_secret&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.readonly
timeZone=America%2FNew_York&description=US+Holidays&colorId=15&accessRole=reader&etag=%22GZxpEFttRDAOmLHnWRxLHHWPGwk%2FkomAuKJmJeZLAvxlc0nIjOqTkKA%22&kind=calendar%23calendarListEntry&foregroundColor=%23000000&summary=US+Holidays&id=en.usa%23holiday%...
eof

my $token = bless( {
                 'refresh_token' => '1/uZmWq1bdeLR5AnjD3yTV1Q93BSxL1wjeulxSzVaAbq8',
                 'expires_in' => '3600',
                 'emailkey' => 'perlmonster@gmail.com',
                 'requested' => 1366410947,
                 'access_token' => 'ya29.Et6DoQjLzjpioHPGbMyDeGfUS00SuTlIrIsZE_FIDzXtU0IJ1-AnAg',
                 'token_type' => 'Bearer'
               }, 'Google::OAuth' );

my $json = <<'eof' ;
{
 "kind": "calendar#calendarList",
 "etag": "\"GZxpEFttRDAOmLHnWRxLHHWPGwk/PlPjmjzcESbTBnWfLUd5E8QtBFI\"",
 "items": [
  {
   "kind": "calendar#calendarListEntry",
   "etag": "\"GZxpEFttRDAOmLHnWRxLHHWPGwk/rlBZV92t2W1zCqj28DRxbmcr5Fs\"",
   "id": "#contacts@group.v.calendar.google.com",
   "summary": "Contacts' birthdays and events",
   "description": "Your contacts' birthdays and anniversaries",
   "timeZone": "America/New_York",
   "colorId": "12",
   "backgroundColor": "#fad165",
   "foregroundColor": "#000000",
   "selected": true,
   "accessRole": "reader"
  },
  {
   "kind": "calendar#calendarListEntry",
   "etag": "\"GZxpEFttRDAOmLHnWRxLHHWPGwk/komAuKJmJeZLAvxlc0nIjOqTkKA\"",
   "id": "en.usa#holiday@group.v.calendar.google.com",
   "summary": "US Holidays",
   "description": "US Holidays",
   "timeZone": "America/New_York",
   "colorId": "15",
   "backgroundColor": "#9fc6e7",
   "foregroundColor": "#000000",
   "selected": true,
   "accessRole": "reader"
  }
 ]
}
eof

my $item = {
               'timeZone' => 'America/New_York',
               'colorId' => '15',
               'description' => 'US Holidays',
               'accessRole' => 'reader',
               'etag' => '"GZxpEFttRDAOmLHnWRxLHHWPGwk/komAuKJmJeZLAvxlc0nIjOqTkKA"',
               'kind' => 'calendar#calendarListEntry',
               'foregroundColor' => '#000000',
               'summary' => 'US Holidays',
               'id' => 'en.usa#holiday@group.v.calendar.google.com',
               'selected' => 'true',
               'backgroundColor' => '#9fc6e7'
             } ;

my $t ;
my $ok ;

is( Google::OAuth->dsn->table, 'googletokens', 'DSN name' ) ;
is( ref Google::OAuth->dsn->db, 'NoSQL::PL2SQL::DBI::Null', 'DSN source' ) ;

$t = Google::OAuth::Client->new->scope(
                'calendar.readonly' 
		)->token_request ;
ok( linkcompare( $t, $test[0] ), 'Default credentials' ) ;

$t = Google::OAuth::Client->new->scope(
		'm8.feeds', 'calendar', 'calendar.readonly', 'drive.readonly', 
		)->token_request ;
ok( linkcompare( $t, $test[1] ), 'Expanded scope' ) ;


my @credentials = qw(
	client_id
	FTNmkFXfh6OZH5jXsW7qLe3bgsnl7ZObPfsuscNy
	client_secret
	Op6MR5gl73VY2yJkrb86dT4iySguvM8HhSqC2dEm
	) ;

is( Google::OAuth->setclient( @credentials ), undef, 'setclient' ) ;

$t = Google::OAuth::Client->new->scope(
                'calendar.readonly' 
		)->token_request ;
ok( linkcompare( $t, $test[2] ), 'Modified credentials' ) ;

$t = Google::OAuth::Client->new(
		'client_id', { foo => 'client_secret' }
		)->scope(
                'calendar.readonly' 
		)->token_request ;
ok( linkcompare( $t, $test[3] ), 'token_request override' ) ;

is( ref $token, 'Google::OAuth', 'Test token found' ) ;
is( $Google::OAuth::Config::test{grantcode}, 
		'1/fk7qwDysHKcwfa2S8ZKWTv2-nwTfxpPva3dzmujc_gQ', 
		'Grant Code found' ) ;

my $event = Google::OAuth::CGI->new( $item )->query_string ;
ok( linkcompare( $event, $test[4] ), 'CGI::Simple object' ) ;



( run in 0.735 second using v1.01-cache-2.11-cpan-5dc5da66d9d )