##@file # Twitter authentication backend file ##@class # Twitter authentication backend class. package Lemonldap::NG::Portal::AuthTwitter; use strict; use Lemonldap::NG::Portal::Simple; use Lemonldap::NG::Portal::_Browser; use URI::Escape; our @ISA = (qw(Lemonldap::NG::Portal::_Browser)); our $VERSION = '2.0.0'; our $initDone; BEGIN { eval { require threads::shared; threads::shared::share($initDone); }; } ## @apmethod int authInit() # @return Lemonldap::NG::Portal constant sub authInit { my $self = shift; return PE_OK if ($initDone); unless ( $self->{twitterKey} and $self->{twitterSecret} ) { $self->abort( 'Bad configuration', 'twitterKey and twitterSecret parameters are required' ); } eval { require Net::OAuth; $Net::OAuth::PROTOCOL_VERSION = &Net::OAuth::PROTOCOL_VERSION_1_0A(); }; $self->abort("Unable to load Net::OAuth: $@") if ($@); $initDone = 1; PE_OK; } ## @apmethod int extractFormInfo() # Authenticate users by Twitter and set user # @return Lemonldap::NG::Portal constant sub extractFormInfo { my $self = shift; my $nonce = time; # Default values for Twitter API $self->{twitterRequestTokenURL} ||= "https://api.twitter.com/oauth/request_token"; $self->{twitterAuthorizeURL} ||= "https://api.twitter.com/oauth/authorize"; $self->{twitterAccessTokenURL} ||= "https://api.twitter.com/oauth/access_token"; # 1. Request to authenticate unless ( $self->param('twitterback') ) { $self->lmLog( 'Redirection to Twitter', 'debug' ); # 1.1 Try to get token to dialog with Twitter my $callback_url = $self->{portal}; # Twitter callback parameter $callback_url .= ( $callback_url =~ /\?/ ? '&' : '?' ) . "twitterback=1"; # Add request state parameters if ( $self->{_url} ) { my $url_param = 'url=' . uri_escape( $self->{_url} ); $callback_url .= ( $callback_url =~ /\?/ ? '&' : '?' ) . $url_param; } if ( $self->param( $self->{authChoiceParam} ) ) { my $url_param = $self->{authChoiceParam} . '=' . uri_escape( $self->param( $self->{authChoiceParam} ) ); $callback_url .= ( $callback_url =~ /\?/ ? '&' : '?' ) . $url_param; } # Forward hidden fields if ( exists $self->{portalHiddenFormValues} ) { $self->lmLog( "Add hidden values to CAS redirect URL\n", 'debug' ); foreach ( keys %{ $self->{portalHiddenFormValues} } ) { $callback_url .= ( $callback_url =~ /\?/ ? '&' : '?' ) . $_ . '=' . uri_escape( $self->{portalHiddenFormValues}->{$_} ); } } my $request = Net::OAuth->request("request token")->new( consumer_key => $self->{twitterKey}, consumer_secret => $self->{twitterSecret}, request_url => $self->{twitterRequestTokenURL}, request_method => 'POST', signature_method => 'HMAC-SHA1', timestamp => time, nonce => $nonce, callback => $callback_url, ); $request->sign; my $request_url = $request->to_url; $self->lmLog( "POST $request_url to Twitter", 'debug' ); my $res = $self->ua()->post($request_url); $self->lmLog( "Twitter response: " . $res->as_string, 'debug' ); if ( $res->is_success ) { my $response = Net::OAuth->response('request token') ->from_post_body( $res->content ); # 1.2 Store token key and secret in cookies push @{ $self->{cookie} }, $self->cookie( -name => '_twitSec', -value => $response->token_secret, -expires => '+3m' ); # 1.3 Redirect user to Twitter my $authorize_url = $self->{twitterAuthorizeURL} . "?oauth_token=" . $response->token; $self->redirect( -uri => $authorize_url ); $self->quit(); } else { $self->lmLog( 'Twitter OAuth protocol error: ' . $res->content, 'error' ); return PE_ERROR; } } # 2. User is back from Twitter my $request_token = $self->param('oauth_token'); my $verifier = $self->param('oauth_verifier'); unless ( $request_token and $verifier ) { $self->lmLog( 'Twitter OAuth protocol error', 'error' ); return PE_ERROR; } $self->lmLog( "Get token $request_token and verifier $verifier from Twitter", 'debug' ); # 2.1 Reconnect to Twitter my $access = Net::OAuth->request("access token")->new( consumer_key => $self->{twitterKey}, consumer_secret => $self->{twitterSecret}, request_url => $self->{twitterAccessTokenURL}, request_method => 'POST', signature_method => 'HMAC-SHA1', verifier => $verifier, token => $request_token, token_secret => $self->cookie('_twitSec'), timestamp => time, nonce => $nonce, ); $access->sign; my $access_url = $access->to_url; $self->lmLog( "POST $access_url to Twitter", 'debug' ); my $res_access = $self->ua()->post($access_url); $self->lmLog( "Twitter response: " . $res_access->as_string, 'debug' ); if ( $res_access->is_success ) { my $response = Net::OAuth->response('access token') ->from_post_body( $res_access->content ); # Get user_id and screename $self->{_twitterUserId} = $response->{extra_params}->{user_id}; $self->{_twitterScreenName} = $response->{extra_params}->{screen_name}; $self->lmLog( "Get user id " . $self->{_twitterUserId} . " and screen name " . $self->{_twitterScreenName}, 'debug' ); } else { $self->lmLog( 'Twitter OAuth protocol error: ' . $res_access->content, 'error' ); return PE_ERROR; } # 2.4 Set $self->{user} to screen name $self->{user} = $self->{_twitterScreenName}; $self->lmLog( "Good Twitter authentication for $self->{user}", 'debug' ); # Force redirection to avoid displaying OAuth datas $self->{mustRedirect} = 1; # Clean temporaries cookies push @{ $self->{cookie} }, $self->cookie( -name => '_twitSec', -value => 0, -expires => '-3m' ); PE_OK; } ## @apmethod int setAuthSessionInfo() # Set authenticationLevel and Twitter attributes. # @return Lemonldap::NG::Portal constant sub setAuthSessionInfo { my $self = shift; $self->{sessionInfo}->{authenticationLevel} = $self->{twitterAuthnLevel}; $self->{sessionInfo}->{'_user'} = $self->{user}; $self->{sessionInfo}->{_twitterUserId} = $self->{_twitterUserId}; $self->{sessionInfo}->{_twitterScreenName} = $self->{_twitterScreenName}; PE_OK; } ## @apmethod int authenticate() # Does nothing. # @return Lemonldap::NG::Portal constant sub authenticate { PE_OK; } ## @apmethod int authFinish() # Does nothing. # @return Lemonldap::NG::Portal constant sub authFinish { PE_OK; } ## @apmethod int authLogout() # Does nothing # @return Lemonldap::NG::Portal constant sub authLogout { PE_OK; } ## @apmethod boolean authForce() # Does nothing # @return result sub authForce { return 0; } ## @method string getDisplayType # @return display type sub getDisplayType { return "logo"; } 1; __END__ =head1 NAME =encoding utf8 Lemonldap::NG::Portal::AuthTwitter - Perl extension for building Lemonldap::NG compatible portals with Twitter authentication. =head1 SYNOPSIS use Lemonldap::NG::Portal::SharedConf; my $portal = new Lemonldap::NG::Portal::Simple( configStorage => {...}, # See Lemonldap::NG::Portal authentication => 'Twitter', ); if($portal->process()) { # Write here the menu with CGI methods. This page is displayed ONLY IF # the user was not redirected here. print $portal->header('text/html; charset=utf-8'); # DON'T FORGET THIS (see CGI(3)) print "..."; } else { # If the user enters here, IT MEANS THAT CAS REDIRECTION DOES NOT WORK print $portal->header('text/html; charset=utf-8'); # DON'T FORGET THIS (see CGI(3)) print "