package session::AUTH::FaceBook; 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 facebook: auto_create_user: 1 app_id: 122117614500563 app_key: 3da06301715b0efc5c873535c56c2c33 app_secret: 656bd1369486b902e9bf831a9a08132b authorize_url: https://graph.facebook.com/oauth/authorize access_token_url: https://graph.facebook.com/oauth/access_token user_info_url: https://graph.facebook.com/me user_post_url: ~ store: class: "+Comments::Authentication::Store" type: facebook =cut our $JSON = JSON::XS->new->utf8; =for rem SCHEMA $m->redirect ( $fb_connect->fb_authorize_url( redirect_uri => ... ) ); =cut sub new { my ($class, %config) = @_; my $self = bless {}, $class; for (qw(facebook_app_id facebook_app_key facebook_app_secret facebook_authorize_url facebook_access_token_url facebook_user_info_url)) { $self->{$_} = $config{$_} || $state->{session}->{$_} || return undef; } $self->{timeout} = $state->{session}->{connection_timeout} || 3; for (qw(facebook_user_post_url facebook_redirect_uri)) { $self->{$_} = $config{$_} || $state->{session}->{$_}; } return $self; } sub fb_authorize_url { my $self = shift; my (%args) = @_; my $go = URI->new( $self->{facebook_authorize_url} ); warn Dumper($go); $go->query_param( client_id => $self->{facebook_app_key} ); $go->query_param( scope => "publish_stream" ); $args{redirect_uri} ||= $self->{facebook_redirect_uri}; for ( keys %args ) { $go->query_param( $_ => $args{$_} ); } $keeper->{session}->store_value( facebook_redirect_url => $self->{facebook_redirect_uri} ); return $go; } sub authenticate { my ( $self, %authinfo ) = @_; warn "FB.authenticate" if $DEBUG; # TODO: we need callback url #warn "user_session=".dumper( $c->user_session )." "; my $local_session = $session || $keeper->{session}->get_session; my $redirect_uri = $local_session->{facebook_redirect_url}; my $access_token = $local_session->{facebook_access_token}; my $expires = $local_session->{facebook_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->{facebook_access_token_url}); $req->query_param( client_id => $self->{facebook_app_id} ); $req->query_param( redirect_uri => $redirect_uri ); $req->query_param( client_secret=> $self->{facebook_app_secret} ); $req->query_param( code => $code); warn "Get $req"; my $res = $ua->get($req); unless ($res->code == 200) { warn "access_token request failed: ".$res->status_line; return undef; } my %res = eval { URI->new("?".$res->content)->query_form }; warn Dumper(\%res); unless ($access_token = $res{access_token}) { warn "No access token in response: ".$res->content; return undef; } $keeper->{session}->store_value( facebook_access_token => $access_token ); $local_session->{facebook_access_token} = $access_token; if( my $expires = $res{expires} ) { $local_session->{facebook_expires} = time + $expires; $keeper->{session}->store_value( facebook_expires => $local_session->{facebook_expires} ); } else { #$c->user_session->{'expires'} = time + 3600*24; } warn "FB: requested access token"; } else { warn "FB: have access token"; } my $req = URI->new( $self->{facebook_user_info_url} ); $req->query_param( access_token => $access_token ); warn "Fetching user $req"; my $res = $ua->get($req); unless ($res->code == 200) { warn "user request failed: ".$res->status_line; return undef; } my $info; unless ( $info = eval { JSON::XS->new->utf8->decode($res->content) } ) { warn "user '".$res->content."' decode failed: $@"; return undef; } warn "Userhash = ".Dumper($info); #warn "facebook: user=$info->{name} / $info->{id} / $info->{gender}"; my @plugins = split (/[\ |\t]+/, $state->{plugins}); if ( grep { $_ eq 'users' } @plugins ) { my $user = $keeper->{users}->get_profile( login => 'facebook:'.$info->{id} ); unless ( ref $user ) { my $user_class = $state->{users}->profile_document_class; $user = $user_class->new( $keeper ); $user->login( 'facebook:'.$info->{id} ); my $name = Encode::encode('utf-8', $info->{name}); Encode::from_to( $name, 'utf-8', 'koi8-r' ); $user->name( $name ); $user->status( 1 ); $user->type( 0 ); $user->login_method('facebook'); $user->country( $info->{locale} ); $user->email( undef ); my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure; if ( ref $prop_ava ) { my $avatar = $user->_store_image( 'https://graph.facebook.com/'.$info->{username}.'/picture?type=large', 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( 'https://graph.facebook.com/'.$info->{username}.'/picture?type=large', attr => 'avatar' ); local $Data::Dumper::Indent = 0; $user->avatar( Data::Dumper::Dumper($avatar) ); $user->store; } } } my %data = ( id => $user->id, name => $user->name, login => $user->login, status => $user->status, type => $user->type, ltime => time, avatar => 'https://graph.facebook.com/'.$info->{username}.'/picture', ); $keeper->{session}->store_value ( %data ); while ( my ( $key, $value ) = each %data ) { $local_session->{$key} = $value; } } return $local_session; } 1;