AuthTwitter.pm 9.91 KB
Newer Older
Yadd's avatar
Yadd committed
1
##@file
Yadd's avatar
Yadd committed
2
# Twitter authentication backend file
Yadd's avatar
Yadd committed
3
4

##@class
Yadd's avatar
Yadd committed
5
# Twitter authentication backend class.
Yadd's avatar
Yadd committed
6
7
8
9
package Lemonldap::NG::Portal::AuthTwitter;

use strict;
use Lemonldap::NG::Portal::Simple;
10
11
use Lemonldap::NG::Portal::_Browser;
use URI::Escape;
Yadd's avatar
Yadd committed
12

13
our @ISA     = (qw(Lemonldap::NG::Portal::_Browser));
Yadd's avatar
Yadd committed
14
our $VERSION = '2.0.0';
Yadd's avatar
Yadd committed
15
our $initDone;
Yadd's avatar
Yadd committed
16

Yadd's avatar
Yadd committed
17
18
19
20
21
22
23
BEGIN {
    eval {
        require threads::shared;
        threads::shared::share($initDone);
    };
}

Yadd's avatar
Yadd committed
24
25
26
27
## @apmethod int authInit()
# @return Lemonldap::NG::Portal constant
sub authInit {
    my $self = shift;
Yadd's avatar
Yadd committed
28
29
    return PE_OK if ($initDone);

Yadd's avatar
Yadd committed
30
31
32
33
    unless ( $self->{twitterKey} and $self->{twitterSecret} ) {
        $self->abort( 'Bad configuration',
            'twitterKey and twitterSecret parameters are required' );
    }
34
35
36
37
38
    eval {
        require Net::OAuth;
        $Net::OAuth::PROTOCOL_VERSION = &Net::OAuth::PROTOCOL_VERSION_1_0A();
    };
    $self->abort("Unable to load Net::OAuth: $@") if ($@);
Yadd's avatar
Yadd committed
39
40

    $initDone = 1;
Yadd's avatar
Yadd committed
41
42
43
44
    PE_OK;
}

## @apmethod int extractFormInfo()
Yadd's avatar
Yadd committed
45
# Authenticate users by Twitter and set user
Yadd's avatar
Yadd committed
46
47
# @return Lemonldap::NG::Portal constant
sub extractFormInfo {
48
49
    my $self  = shift;
    my $nonce = time;
Yadd's avatar
Yadd committed
50

51
52
53
54
55
56
    # 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";
Yadd's avatar
Yadd committed
57
58

    # 1. Request to authenticate
Yadd's avatar
Yadd committed
59
60
    unless ( $self->param('twitterback') ) {
        $self->lmLog( 'Redirection to Twitter', 'debug' );
Yadd's avatar
Yadd committed
61
62

        # 1.1 Try to get token to dialog with Twitter
63
        my $callback_url = $self->url();
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

        # 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} ) {

Yadd's avatar
Yadd committed
84
85
            $self->lmLog( "Add hidden values to Twitter redirect URL",
                'debug' );
86
87
88
89
90
91

            foreach ( keys %{ $self->{portalHiddenFormValues} } ) {
                $callback_url .=
                    ( $callback_url =~ /\?/ ? '&' : '?' )
                  . $_ . '='
                  . uri_escape( $self->{portalHiddenFormValues}->{$_} );
Yadd's avatar
Yadd committed
92
93
            }
        }
Yadd's avatar
Yadd committed
94

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
        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;
        }
Yadd's avatar
Yadd committed
138
    }
Yadd's avatar
Yadd committed
139
140

    # 2. User is back from Twitter
Yadd's avatar
Yadd committed
141
142
    my $request_token = $self->param('oauth_token');
    my $verifier      = $self->param('oauth_verifier');
Yadd's avatar
Yadd committed
143
144
    unless ( $request_token and $verifier ) {
        $self->lmLog( 'Twitter OAuth protocol error', 'error' );
Yadd's avatar
Yadd committed
145
146
        return PE_ERROR;
    }
Yadd's avatar
Yadd committed
147

148
149
150
151
    $self->lmLog(
        "Get token $request_token and verifier $verifier from Twitter",
        'debug' );

Yadd's avatar
Yadd committed
152
    # 2.1 Reconnect to Twitter
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
    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;
Yadd's avatar
Yadd committed
194
    }
Yadd's avatar
Yadd committed
195

196
197
    # 2.4 Set $self->{user} to screen name
    $self->{user} = $self->{_twitterScreenName};
Yadd's avatar
Yadd committed
198
    $self->lmLog( "Good Twitter authentication for $self->{user}", 'debug' );
Yadd's avatar
Yadd committed
199
200

    # Force redirection to avoid displaying OAuth datas
Yadd's avatar
Yadd committed
201
    $self->{mustRedirect} = 1;
Yadd's avatar
Yadd committed
202
203
204
205

    # Clean temporaries cookies
    push @{ $self->{cookie} },
      $self->cookie( -name => '_twitSec', -value => 0, -expires => '-3m' );
Yadd's avatar
Yadd committed
206
207
208
209
    PE_OK;
}

## @apmethod int setAuthSessionInfo()
Clément OUDOT's avatar
   
Clément OUDOT committed
210
# Set authenticationLevel and Twitter attributes.
Yadd's avatar
Yadd committed
211
212
213
214
# @return Lemonldap::NG::Portal constant
sub setAuthSessionInfo {
    my $self = shift;

215
    $self->{sessionInfo}->{authenticationLevel} = $self->{twitterAuthnLevel};
216
217
218
    $self->{sessionInfo}->{'_user'}             = $self->{user};
    $self->{sessionInfo}->{_twitterUserId}      = $self->{_twitterUserId};
    $self->{sessionInfo}->{_twitterScreenName}  = $self->{_twitterScreenName};
Clément OUDOT's avatar
   
Clément OUDOT committed
219

Yadd's avatar
Yadd committed
220
221
222
223
224
225
226
227
228
229
    PE_OK;
}

## @apmethod int authenticate()
# Does nothing.
# @return Lemonldap::NG::Portal constant
sub authenticate {
    PE_OK;
}

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
## @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;
}

251
252
253
254
255
256
## @method string getDisplayType
# @return display type
sub getDisplayType {
    return "logo";
}

Yadd's avatar
Yadd committed
257
258
259
260
261
262
263
1;
__END__

=head1 NAME

=encoding utf8

Yadd's avatar
Yadd committed
264
265
Lemonldap::NG::Portal::AuthTwitter - Perl extension for building Lemonldap::NG
compatible portals with Twitter authentication.
Yadd's avatar
Yadd committed
266
267
268
269
270
271

=head1 SYNOPSIS

  use Lemonldap::NG::Portal::SharedConf;
  my $portal = new Lemonldap::NG::Portal::Simple(
         configStorage     => {...}, # See Lemonldap::NG::Portal
Yadd's avatar
Yadd committed
272
         authentication    => 'Twitter',
Yadd's avatar
Yadd committed
273
274
275
276
277
    );

  if($portal->process()) {
    # Write here the menu with CGI methods. This page is displayed ONLY IF
    # the user was not redirected here.
Yadd's avatar
Yadd committed
278
    print $portal->header('text/html; charset=utf-8'); # DON'T FORGET THIS (see CGI(3))
Yadd's avatar
Yadd committed
279
280
281
282
    print "...";
  }
  else {
    # If the user enters here, IT MEANS THAT CAS REDIRECTION DOES NOT WORK
Yadd's avatar
Yadd committed
283
    print $portal->header('text/html; charset=utf-8'); # DON'T FORGET THIS (see CGI(3))
Yadd's avatar
Yadd committed
284
285
286
287
288
289
290
291
    print "<html><body><h1>Unable to work</h1>";
    print "This server isn't well configured. Contact your administrator.";
    print "</body></html>";
  }

=head1 DESCRIPTION

This library just overload few methods of Lemonldap::NG::Portal::Simple to use
Yadd's avatar
Yadd committed
292
Twitter authentication mechanism.
Yadd's avatar
Yadd committed
293
294
295
296
297
298

See L<Lemonldap::NG::Portal::Simple> for usage and other methods.

=head1 SEE ALSO

L<Lemonldap::NG::Portal>, L<Lemonldap::NG::Portal::Simple>,
Yadd's avatar
Yadd committed
299
L<http://lemonldap-ng.org/>
Yadd's avatar
Yadd committed
300
301
302

=head1 AUTHOR

303
304
305
306
307
308
309
=over

=item Clement Oudot, E<lt>clem.oudot@gmail.comE<gt>

=item Xavier Guimard, E<lt>x.guimard@free.frE<gt>

=back
Yadd's avatar
Yadd committed
310
311
312
313

=head1 BUG REPORT

Use OW2 system to report bug or ask for features:
Yadd's avatar
Yadd committed
314
L<http://jira.ow2.org>
Yadd's avatar
Yadd committed
315
316
317
318
319
320
321
322

=head1 DOWNLOAD

Lemonldap::NG is available at
L<http://forge.objectweb.org/project/showfiles.php?group_id=274>

=head1 COPYRIGHT AND LICENSE

323
324
325
326
=over

=item Copyright (C) 2010 by Xavier Guimard, E<lt>x.guimard@free.frE<gt>

Yadd's avatar
Yadd committed
327
=item Copyright (C) 2010-2012 by Clement Oudot, E<lt>clem.oudot@gmail.comE<gt>
328
329

=back
Yadd's avatar
Yadd committed
330
331

This library is free software; you can redistribute it and/or modify
332
333
334
335
336
337
338
339
340
341
342
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see L<http://www.gnu.org/licenses/>.
Yadd's avatar
Yadd committed
343
344
345
346

=cut