Revision 312 (by ahitrov, 2013/04/24 18:06:57) |
Vkontakte credential auth
|
package session::AUTH::VKontakte;
use strict;
use warnings;
use LWP::UserAgent;
use JSON::XS;
use Data::Dumper;
use URI;
use URI::QueryParam;
use Encode;
use Contenido::Globals;
use vars qw($VERSION);
$VERSION = '4.1';
=for rem
vkontakte:
auto_create_user: 1
app_id: decimal digits
app_secret: 32 hex digits
authorize_url: http://api.vkontakte.ru/oauth/authorize
access_token_url: https://api.vkontakte.ru/oauth/access_token
user_info_url: https://api.vkontakte.ru/method/getProfiles
user_post_url: ~
=cut
our $JSON = JSON::XS->new->utf8;
=for rem SCHEMA
$m->redirect ( $vk_connect->authorize_url( vk_redirect_uri => ... )->as_string );
=cut
sub new {
my ($class, %config) = @_;
my $self = bless {}, $class;
$self->{vk_authorize_url} = 'http://oauth.vk.com/authorize';
$self->{vk_access_token_url} = 'https://oauth.vk.com/access_token';
$self->{vk_user_info_url} = 'https://api.vk.com/method/getProfiles';
for (qw( vk_app_id vk_app_secret )) {
$self->{$_} = $config{$_} || $state->{session}->{$_} || return undef;
}
$self->{timeout} = $state->{session}->{connection_timeout} || 3;
for (qw(vk_user_post_url vk_redirect_uri)) {
$self->{$_} = $config{$_} || $state->{session}->{$_};
}
return $self;
}
sub authorize_url {
my $self = shift;
my (%args) = @_;
my $go = URI->new( $self->{vk_authorize_url} );
$go->query_param( client_id => $self->{vk_app_id} );
$go->query_param( scope => '' );
$go->query_param( response_type => 'code' );
$args{redirect_uri} ||= $self->{vk_redirect_uri};
for ( keys %args ) {
$go->query_param( $_ => $args{$_} );
}
my $local_session = $session || $keeper->{session}->get_session;
$local_session->set( vk_redirect_url => $self->{vk_redirect_uri} );
return $go;
}
sub authenticate {
my ( $self, %authinfo ) = @_;
warn "VK.authenticate" if $DEBUG;
my $local_session = $session || $keeper->{session}->get_session;
my $redirect_uri = $self->{vk_redirect_uri};
my $access_token = $local_session->{vk_access_token};
my $vk_user_id = $local_session->{vk_user_id};
my $expires = $local_session->{vk_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\n";
return undef;
}
my $ua = LWP::UserAgent->new;
$ua->timeout($self->{timeout});
unless ($access_token) {
my $req = URI->new( $self->{vk_access_token_url});
$req->query_param( client_id => $self->{vk_app_id} );
$req->query_param( client_secret => $self->{vk_app_secret} );
$req->query_param( code => $code );
$req->query_param( redirect_uri => $redirect_uri );
warn "Token request: [$req]\n" if $DEBUG;
my $res = $ua->get($req);
unless ($res->code == 200) {
warn "VK: Access_token request failed: ".$res->status_line."\n";
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;
}
$local_session->set( vk_access_token => $access_token );
$local_session->set( vk_user_id => $info->{user_id} );
if ( my $expires = $info->{expires_in} ) {
$local_session->set( vk_expires => time + $expires );
} else {
#$local_session->set( vk_expires => time + 3600*24 );
}
warn "VK: requested access token" if $DEBUG;
} else {
warn "VK: have access token" if $DEBUG;
}
my $req = URI->new( $self->{vk_user_info_url} );
$req->query_param( uid => $local_session->{vk_user_id} );
$req->query_param( fields => 'uid,first_name,last_name,nickname,domain,sex,bdate,city,country,timezone,photo,photo_medium,photo_big' );
$req->query_param( access_token => $access_token );
warn "VK: Fetching user $req\n" if $DEBUG;
my $res = $ua->get($req);
unless ($res->code == 200) {
warn "VK: user request failed: ".$res->status_line."\n";
return undef;
}
my $info;
unless ( $info = eval { $JSON->decode($res->content) } ) {
warn "user '".$res->content."' decode failed: $@\n";
return undef;
}
warn Dumper($info) if $DEBUG;
return undef unless exists $info->{response} && ref $info->{response} eq 'ARRAY' && @{$info->{response}};
my $user_info = $info->{response}[0];
foreach my $key ( qw(nickname last_name first_name) ) {
$user_info->{$key} = Encode::encode('utf-8', $user_info->{$key});
}
my @plugins = split (/[\ |\t]+/, $state->{plugins});
my $name = $user_info->{first_name}.' '.$user_info->{last_name};
if ( grep { $_ eq 'users' } @plugins ) {
my $user;
if ( $state->{users}->use_credentials ) {
$user = $keeper->{users}->get_profile( vkontakte => $user_info->{uid} );
}
unless ( ref $user ) {
$user = $keeper->{users}->get_profile( login => 'vkontakte:'.$user_info->{uid} );
}
unless ( ref $user ) {
my $user_class = $state->{users}->profile_document_class;
$user = $user_class->new( $keeper );
$user->login( 'vkontakte:'.$user_info->{uid} );
$user->name( $user_info->{last_name}.', '.$user_info->{first_name} );
$user->nickname( $user_info->{nickname} );
$user->status( 1 );
$user->type( 0 );
$user->login_method('vkontakte');
$user->country( $user_info->{country} );
$user->email( undef );
my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure;
if ( ref $prop_ava ) {
my $avatar = $user->_store_image( $user_info->{photo_big}, attr => 'avatar' );
local $Data::Dumper::Indent = 0;
$user->avatar( Data::Dumper::Dumper($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' );
unless ( ref $avatar && exists $avatar->{filename} ) {
my $avatar = $user->_store_image( $user_info->{photo_big}, attr => 'avatar' );
local $Data::Dumper::Indent = 0;
$user->avatar( Data::Dumper::Dumper($avatar) );
$user->store;
}
}
}
if ( $state->{users}->use_credentials ) {
$user->create_credential(
vkontakte => $user_info->{uid},
avatar => $user_info->{photo_big},
);
}
my %data = (
id => $user->id,
name => $name,
login => $user->login,
status => $user->status,
type => $user->type,
auth_by => 'vkontakte',
ltime => time,
);
if ( $user_info->{photo} ) {
$data{avatar} = $user_info->{photo};
}
$local_session->set( %data );
} else {
my %data = (
id => $user_info->{uid},
name => $name,
nick => $user_info->{nickname} || $name,
login => 'vkontakte:'.$user_info->{uid},
status => 1,
type => 0,
auth_by => 'vkontakte',
ltime => time,
);
if ( $user_info->{photo} ) {
$data{avatar} = $user_info->{photo};
}
$local_session->set( %data );
}
return $local_session;
}
1;