| 1 | 191 | ahitrov | package session::AUTH::FaceBook; | 
   
        | 2 |  |  |  | 
   
        | 3 |  |  | use strict; | 
   
        | 4 |  |  | use warnings; | 
   
        | 5 |  |  | use LWP::UserAgent; | 
   
        | 6 |  |  | use JSON::XS; | 
   
        | 7 |  |  | use Data::Dumper; | 
   
        | 8 |  |  | use URI; | 
   
        | 9 |  |  | use URI::QueryParam; | 
   
        | 10 |  |  | use Encode; | 
   
        | 11 |  |  | use Contenido::Globals; | 
   
        | 12 |  |  |  | 
   
        | 13 |  |  | use vars qw($VERSION); | 
   
        | 14 |  |  | $VERSION = '4.1'; | 
   
        | 15 |  |  |  | 
   
        | 16 |  |  | =for rem | 
   
        | 17 |  |  | facebook: | 
   
        | 18 |  |  | auto_create_user: 1 | 
   
        | 19 |  |  | app_id: 122117614500563 | 
   
        | 20 |  |  | app_key: 3da06301715b0efc5c873535c56c2c33 | 
   
        | 21 |  |  | app_secret: 656bd1369486b902e9bf831a9a08132b | 
   
        | 22 |  |  | authorize_url: https://graph.facebook.com/oauth/authorize | 
   
        | 23 |  |  | access_token_url: https://graph.facebook.com/oauth/access_token | 
   
        | 24 |  |  | user_info_url: https://graph.facebook.com/me | 
   
        | 25 |  |  | user_post_url: ~ | 
   
        | 26 |  |  | store: | 
   
        | 27 |  |  | class: "+Comments::Authentication::Store" | 
   
        | 28 |  |  | type: facebook | 
   
        | 29 |  |  |  | 
   
        | 30 |  |  | =cut | 
   
        | 31 |  |  |  | 
   
        | 32 |  |  | our $JSON = JSON::XS->new->utf8; | 
   
        | 33 |  |  |  | 
   
        | 34 |  |  | =for rem SCHEMA | 
   
        | 35 |  |  |  | 
   
        | 36 |  |  | $m->redirect ( $fb_connect->fb_authorize_url( redirect_uri => ... ) ); | 
   
        | 37 |  |  |  | 
   
        | 38 |  |  |  | 
   
        | 39 |  |  | =cut | 
   
        | 40 |  |  |  | 
   
        | 41 |  |  |  | 
   
        | 42 |  |  | sub new { | 
   
        | 43 |  |  | my ($class, %config) = @_; | 
   
        | 44 |  |  | my $self = bless {}, $class; | 
   
        | 45 |  |  | for (qw(facebook_app_id facebook_app_key facebook_app_secret facebook_authorize_url facebook_access_token_url facebook_user_info_url)) { | 
   
        | 46 |  |  | $self->{$_} = $config{$_} || $state->{session}->{$_} || return undef; | 
   
        | 47 |  |  | } | 
   
        | 48 |  |  | $self->{timeout} = $state->{session}->{connection_timeout} || 3; | 
   
        | 49 |  |  | for (qw(facebook_user_post_url facebook_redirect_uri)) { | 
   
        | 50 |  |  | $self->{$_} = $config{$_} || $state->{session}->{$_}; | 
   
        | 51 |  |  | } | 
   
        | 52 |  |  | return $self; | 
   
        | 53 |  |  | } | 
   
        | 54 |  |  |  | 
   
        | 55 |  |  | sub fb_authorize_url { | 
   
        | 56 |  |  | my $self = shift; | 
   
        | 57 |  |  | my (%args) = @_; | 
   
        | 58 |  |  | my $go = URI->new( $self->{facebook_authorize_url} ); | 
   
        | 59 |  |  | warn Dumper($go); | 
   
        | 60 |  |  | $go->query_param( client_id => $self->{facebook_app_key} ); | 
   
        | 61 |  |  | $go->query_param( scope => "publish_stream" ); | 
   
        | 62 |  |  | $args{redirect_uri} ||= $self->{facebook_redirect_uri}; | 
   
        | 63 |  |  | for ( keys %args ) { | 
   
        | 64 |  |  | $go->query_param( $_ => $args{$_} ); | 
   
        | 65 |  |  | } | 
   
        | 66 |  |  | $keeper->{session}->store_value( facebook_redirect_url => $self->{facebook_redirect_uri} ); | 
   
        | 67 |  |  | return $go; | 
   
        | 68 |  |  | } | 
   
        | 69 |  |  |  | 
   
        | 70 |  |  | sub authenticate { | 
   
        | 71 |  |  | my ( $self, %authinfo ) = @_; | 
   
        | 72 |  |  | warn "FB.authenticate"					if $DEBUG; | 
   
        | 73 |  |  | # TODO: we need callback url | 
   
        | 74 |  |  | #warn "user_session=".dumper( $c->user_session )." "; | 
   
        | 75 |  |  | my $local_session = $session || $keeper->{session}->get_session; | 
   
        | 76 |  |  | my $redirect_uri = $local_session->{facebook_redirect_url}; | 
   
        | 77 |  |  |  | 
   
        | 78 |  |  | my $access_token = $local_session->{facebook_access_token}; | 
   
        | 79 |  |  | my $expires = $local_session->{facebook_expires}; | 
   
        | 80 |  |  | if ($access_token and $expires > time) { | 
   
        | 81 |  |  | warn "Already have access_token"		if $DEBUG; | 
   
        | 82 |  |  | } else { | 
   
        | 83 |  |  | undef $access_token; | 
   
        | 84 |  |  | } | 
   
        | 85 |  |  | my $code = $authinfo{'code'}; | 
   
        | 86 |  |  | unless ( $code ) { | 
   
        | 87 |  |  | warn "Call to authenticate without code"; | 
   
        | 88 |  |  | return undef; | 
   
        | 89 |  |  | } | 
   
        | 90 |  |  | my $ua = LWP::UserAgent->new; | 
   
        | 91 |  |  | $ua->timeout($self->{timeout}); | 
   
        | 92 |  |  | unless ($access_token) { | 
   
        | 93 |  |  | my $req = URI->new( $self->{facebook_access_token_url}); | 
   
        | 94 |  |  | $req->query_param( client_id	=> $self->{facebook_app_id} ); | 
   
        | 95 |  |  | $req->query_param( redirect_uri	=> $redirect_uri ); | 
   
        | 96 |  |  | $req->query_param( client_secret=> $self->{facebook_app_secret} ); | 
   
        | 97 |  |  | $req->query_param( code		=> $code); | 
   
        | 98 |  |  | warn "Get $req"; | 
   
        | 99 |  |  | my $res = $ua->get($req); | 
   
        | 100 |  |  | unless ($res->code == 200) { | 
   
        | 101 |  |  | warn "access_token request failed: ".$res->status_line; | 
   
        | 102 |  |  | return undef; | 
   
        | 103 |  |  | } | 
   
        | 104 |  |  | my %res = eval { URI->new("?".$res->content)->query_form }; | 
   
        | 105 |  |  | warn Dumper(\%res); | 
   
        | 106 |  |  | unless ($access_token = $res{access_token}) { | 
   
        | 107 |  |  | warn "No access token in response: ".$res->content; | 
   
        | 108 |  |  | return undef; | 
   
        | 109 |  |  | } | 
   
        | 110 |  |  | $keeper->{session}->store_value( facebook_access_token => $access_token ); | 
   
        | 111 |  |  | $local_session->{facebook_access_token} = $access_token; | 
   
        | 112 |  |  | if( my $expires = $res{expires} ) { | 
   
        | 113 |  |  | $local_session->{facebook_expires} = time + $expires; | 
   
        | 114 |  |  | $keeper->{session}->store_value( facebook_expires => $local_session->{facebook_expires} ); | 
   
        | 115 |  |  | } else { | 
   
        | 116 |  |  | #$c->user_session->{'expires'} = time + 3600*24; | 
   
        | 117 |  |  | } | 
   
        | 118 |  |  | warn "FB: requested access token"; | 
   
        | 119 |  |  | } else { | 
   
        | 120 |  |  | warn "FB: have access token"; | 
   
        | 121 |  |  | } | 
   
        | 122 |  |  |  | 
   
        | 123 |  |  | my $req = URI->new( $self->{facebook_user_info_url} ); | 
   
        | 124 |  |  | $req->query_param( access_token => $access_token ); | 
   
        | 125 |  |  |  | 
   
        | 126 |  |  | warn "Fetching user $req"; | 
   
        | 127 |  |  | my $res = $ua->get($req); | 
   
        | 128 |  |  | unless ($res->code == 200) { | 
   
        | 129 |  |  | warn "user request failed: ".$res->status_line; | 
   
        | 130 |  |  | return undef; | 
   
        | 131 |  |  | } | 
   
        | 132 |  |  | my $info; | 
   
        | 133 |  |  | unless ( $info = eval { JSON::XS->new->utf8->decode($res->content) } ) { | 
   
        | 134 |  |  | warn "user '".$res->content."' decode failed: $@"; | 
   
        | 135 |  |  | return undef; | 
   
        | 136 |  |  | } | 
   
        | 137 |  |  | warn "Userhash = ".Dumper($info); | 
   
        | 138 |  |  | #warn "facebook: user=$info->{name} / $info->{id} / $info->{gender}"; | 
   
        | 139 |  |  |  | 
   
        | 140 |  |  | my @plugins = split (/[\ |\t]+/, $state->{plugins}); | 
   
        | 141 |  |  | if ( grep { $_ eq 'users' } @plugins ) { | 
   
        | 142 |  |  | my $user = $keeper->{users}->get_profile( login => 'facebook:'.$info->{id} ); | 
   
        | 143 |  |  | unless ( ref $user ) { | 
   
        | 144 |  |  | my $user_class = $state->{users}->profile_document_class; | 
   
        | 145 |  |  | $user = $user_class->new( $keeper ); | 
   
        | 146 |  |  | $user->login( 'facebook:'.$info->{id} ); | 
   
        | 147 |  |  | my $name = Encode::encode('utf-8', $info->{name}); | 
   
        | 148 |  |  | Encode::from_to( $name, 'utf-8', 'koi8-r' ); | 
   
        | 149 |  |  | $user->name( $name ); | 
   
        | 150 |  |  | $user->status( 1 ); | 
   
        | 151 |  |  | $user->type( 0 ); | 
   
        | 152 |  |  | $user->login_method('facebook'); | 
   
        | 153 |  |  | $user->country( $info->{locale} ); | 
   
        | 154 |  |  | $user->email( undef ); | 
   
        | 155 |  |  |  | 
   
        | 156 |  |  | my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure; | 
   
        | 157 |  |  | if ( ref $prop_ava ) { | 
   
        | 158 |  |  | my $avatar = $user->_store_image( 'https://graph.facebook.com/'.$info->{username}.'/picture?type=large', attr => 'avatar' ); | 
   
        | 159 |  |  | local $Data::Dumper::Indent = 0; | 
   
        | 160 |  |  | $user->avatar( Data::Dumper::Dumper($avatar) ); | 
   
        | 161 |  |  | } | 
   
        | 162 |  |  |  | 
   
        | 163 |  |  | $user->store; | 
   
        | 164 |  |  | } else { | 
   
        | 165 |  |  | my ($prop_ava) = grep { $_->{attr} eq 'avatar' && $_->{type} eq 'image' } $user->structure; | 
   
        | 166 |  |  | if ( ref $prop_ava ) { | 
   
        | 167 |  |  | my $avatar = $user->get_image( 'avatar' ); | 
   
        | 168 |  |  | unless ( ref $avatar && exists $avatar->{filename} ) { | 
   
        | 169 |  |  | my $avatar = $user->_store_image( 'https://graph.facebook.com/'.$info->{username}.'/picture?type=large', attr => 'avatar' ); | 
   
        | 170 |  |  | local $Data::Dumper::Indent = 0; | 
   
        | 171 |  |  | $user->avatar( Data::Dumper::Dumper($avatar) ); | 
   
        | 172 |  |  | $user->store; | 
   
        | 173 |  |  | } | 
   
        | 174 |  |  | } | 
   
        | 175 |  |  | } | 
   
        | 176 |  |  | my %data = ( | 
   
        | 177 |  |  | id	=> $user->id, | 
   
        | 178 |  |  | name	=> $user->name, | 
   
        | 179 |  |  | login	=> $user->login, | 
   
        | 180 |  |  | status	=> $user->status, | 
   
        | 181 |  |  | type	=> $user->type, | 
   
        | 182 |  |  | ltime   => time, | 
   
        | 183 |  |  | avatar	=> 'https://graph.facebook.com/'.$info->{username}.'/picture', | 
   
        | 184 |  |  | ); | 
   
        | 185 |  |  | $keeper->{session}->store_value ( %data ); | 
   
        | 186 |  |  | while ( my ( $key, $value ) = each %data ) { | 
   
        | 187 |  |  | $local_session->{$key} = $value; | 
   
        | 188 |  |  | } | 
   
        | 189 |  |  | } | 
   
        | 190 |  |  | return $local_session; | 
   
        | 191 |  |  | } | 
   
        | 192 |  |  |  | 
   
        | 193 |  |  | 1; |