Revision 370 (by ahitrov, 2013/07/16 16:27:05) |
Country property check
|
package session::AUTH::Google;
use strict;
use warnings;
use LWP::UserAgent;
use JSON::XS;
use Data::Dumper;
use URI;
use URI::QueryParam;
use Encode;
use Digest::MD5 qw/ md5_hex /;
use Contenido::Globals;
use vars qw($VERSION);
$VERSION = '4.1';
=for rem
facebook:
auto_create_user: 1
app_id: string
app_secret: 32 hex digits
authorize_url: https://accounts.google.com/o/oauth2/auth
access_token_url: https://accounts.google.com/o/oauth2/token
user_info_url: https://www.googleapis.com/oauth2/v1/userinfo
user_post_url: ~
state: is passed back to your app as a parameter of the redirect_uri when the user completed the authentication
store:
class: "+Comments::Authentication::Store"
type: facebook
=cut
our $JSON = JSON::XS->new->utf8;
=for rem SCHEMA
$m->redirect ( $g_connect->authorize_url( redirect_uri => ... )->as_string );
=cut
sub new {
my ($class, %config) = @_;
my $self = bless {}, $class;
$self->{google_authorize_url} = 'https://accounts.google.com/o/oauth2/auth';
$self->{google_access_token_url} = 'https://accounts.google.com/o/oauth2/token';
$self->{google_user_info_url} = 'https://www.googleapis.com/oauth2/v2/userinfo';
# $self->{scope} = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email';
$self->{scope} = $config{scope} || $state->{session}{google_scope} || 'https://www.googleapis.com/auth/userinfo.profile';
for (qw(google_app_id google_app_secret)) {
$self->{$_} = $config{$_} || $state->{session}{$_} || return undef;
}
$self->{timeout} = $state->{session}{connection_timeout} || 3;
for (qw(google_user_post_url google_redirect_uri)) {
$self->{$_} = $config{$_} || $state->{session}{$_};
}
return $self;
}
sub authorize_url {
my $self = shift;
my (%args) = @_;
my $go = URI->new( $self->{google_authorize_url} );
$go->query_param( client_id => $self->{google_app_id} );
# $go->query_param( state => $args{state} ) if $args{state};
$go->query_param( response_type => "code" );
$go->query_param( scope => $self->{scope} );
$args{redirect_uri} ||= $self->{google_redirect_uri};
for ( keys %args ) {
$go->query_param( $_ => $args{$_} );
}
warn Dumper($go) if $DEBUG;
return $go;
}
sub authenticate {
my ( $self, %authinfo ) = @_;
warn "Google.authenticate" if $DEBUG;
my $local_session = $session || $keeper->{session}->get_session;
my $redirect_uri = $self->{google_redirect_uri};
my $access_token = $local_session->{google_access_token} || $local_session->{google_refresh_token};
my $expires = $local_session->{google_expires};
if ($access_token and $expires > time) {
warn "Already have access_token" if $DEBUG;
} else {
undef $access_token;
}
my $code = $authinfo{'code'};
unless ( $code ) {
warn "Call to authenticate without code";
return undef;
}
my $ua = LWP::UserAgent->new;
$ua->timeout($self->{timeout});
unless ($access_token) {
my $req = URI->new( $self->{google_access_token_url});
my %post_params = (
code => $code,
client_id => $self->{google_app_id},
client_secret => $self->{google_app_secret},
redirect_uri => $redirect_uri,
grant_type => 'authorization_code',
);
warn "Post: $req".Dumper(\%post_params) if $DEBUG;
my $res = $ua->post($req, \%post_params);
unless ($res->code == 200) {
warn "access_token request failed: ".$res->status_line;
return undef;
}
my $info = $JSON->decode($res->content);
unless ( ref $info eq 'HASH' && ($access_token = $info->{access_token}) ) {
warn "No access token in response: ".$res->content."\n";
return undef;
}
$keeper->{session}->store_value(
google_access_token => $access_token,
google_refresh_token => $info->{refresh_token},
);
$local_session->{google_access_token} = $access_token;
$local_session->{google_refresh_token} = $info->{refresh_token} if $info->{refresh_token};
if( my $expires = $info->{expires_in} ) {
$local_session->{google_expires} = time + $expires;
$keeper->{session}->store_value( google_expires => $local_session->{google_expires} );
} else {
#$c->user_session->{'expires'} = time + 3600*24;
}
warn "Google: requested access token: $access_token" if $DEBUG;
} else {
warn "Google: have access token" if $DEBUG;
}
my $req = URI->new( $self->{google_user_info_url} );
$req->query_param( access_token => $access_token );
$ua->credentials("googleapis.com:80", "Authorization", "Bearer", $access_token);
warn "Fetching user $req" if $DEBUG;
my $res = $ua->get($req);
unless ($res->code == 200) {
warn "user request failed: ".$res->status_line;
return undef;
}
my $info;
unless ( $info = eval { $JSON->decode($res->content) } ) {
warn "user '".$res->content."' decode failed: $@";
return undef;
}
foreach my $key ( qw(name family_name given_name) ) {
$info->{$key} = Encode::encode('utf-8', $info->{$key});
}
warn "Userhash = ".Dumper($info) if $DEBUG;
my @plugins = split (/[\ |\t]+/, $state->{plugins});
my $name = $info->{name};
if ( grep { $_ eq 'users' } @plugins ) {
my $user = $keeper->{users}->get_profile( email => $info->{email} ) if $info->{email};
$user ||= $keeper->{users}->get_profile( login => 'google:'.$info->{id} );
unless ( ref $user ) {
my $user_class = $state->{users}->profile_document_class;
$user = $user_class->new( $keeper );
my %props = map { $_->{attr} => $_ } $user->structure;
$user->login( $info->{email} || 'google:'.$info->{id} );
$user->name( $name );
$user->status( 1 );
$user->type( 0 );
$user->login_method('google');
if ( exists $props{country} && $info->{locale} ) {
$user->country( $info->{locale} );
}
if ( $info->{birthday} && $info->{birthday} =~ /(\d{2})\.(\d{2})\.(\d{4})/ ) {
$user->dtime( "$3-$2-$1" );
}
$user->email( $info->{email} || undef );
my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure;
if ( ref $prop_ava && $info->{picture} ) {
my $avatar = $user->_store_image( $info->{picture}, attr => 'avatar' );
$user->avatar( $user->_serialize($avatar) );
}
$user->store;
} else {
my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure;
if ( ref $prop_ava ) {
my $avatar = $user->get_image( 'avatar' );
if ( $info->{picture} && !(ref $avatar && exists $avatar->{filename}) ) {
my $avatar = $user->_store_image( $info->{picture}, attr => 'avatar' );
$user->avatar( $user->_serialize($avatar) );
$user->store;
}
}
}
my %data = (
id => $user->id,
name => $user->name,
login => $user->login,
email => $user->email,
status => $user->status,
type => $user->type,
ltime => time,
avatar => $info->{picture},
);
$keeper->{session}->store_value ( %data );
while ( my ( $key, $value ) = each %data ) {
$local_session->{$key} = $value;
}
} else {
my %data = (
id => $info->{id},
name => $name,
gender => $info->{gender} ? ($info->{gender} eq 'male' ? 'm' : $info->{gender} eq 'female' ? 'f' : undef) : undef,
email => $info->{email},
login => $info->{email} || 'google:'.$info->{id},
status => 1,
type => 0,
auth_by => 'google',
ltime => time,
);
if ( $user->{picture} ) {
$data{avatar} = $info->{picture};
}
$keeper->{session}->store_value ( %data );
while ( my ( $key, $value ) = each %data ) {
$local_session->{$key} = $value;
}
}
return $local_session;
}
1;