Facebook.pm 4.73 KB
Newer Older
Yadd's avatar
Yadd committed
1 2 3 4 5 6 7 8 9 10 11
package Lemonldap::NG::Portal::Auth::Facebook;

use strict;
use Mouse;
use URI::Escape;
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK PE_ERROR PE_BADCREDENTIALS);

our $VERSION = '2.0.0';

extends 'Lemonldap::NG::Portal::Auth::Base';

Yadd's avatar
Yadd committed
12 13
# INITIALIZATION

Yadd's avatar
Yadd committed
14 15 16 17
sub init {
    my ($self) = @_;
    eval { require Net::Facebook::Oauth2; };
    if ($@) {
Yadd's avatar
Yadd committed
18
        $self->error("Unable to load Net::Facebook::Oauth2: $@");
Yadd's avatar
Yadd committed
19 20 21 22 23 24
        return 0;
    }
    my $ret = 1;
    foreach my $arg (qw(facebookAppId facebookAppSecret)) {
        unless ( $self->conf->{$arg} ) {
            $ret = 0;
Yadd's avatar
Yadd committed
25
            $self->error("Parameter $arg is required");
Yadd's avatar
Yadd committed
26 27 28 29 30
        }
    }
    return $ret;
}

Yadd's avatar
Yadd committed
31 32
# RUNNING METHODS

Yadd's avatar
Yadd committed
33 34 35 36 37 38 39 40
sub extractFormInfo {
    my ( $self, $req ) = @_;
    my $fb = $self->fb($req);

    # 1. Check Facebook responses

    # 1.1 Good responses
    if ( my $code = $req->param('code') ) {
41 42 43 44 45 46 47 48
        $self->logger->debug("Get code $code from Facebook");
        my $access_token;
        eval { $access_token = $fb->get_access_token( code => $code ); };
        if ($@) {
            $self->logger->error("Error while getting access token: $@");
            return PE_ERROR;
        }
        if ($access_token) {
Yadd's avatar
Yadd committed
49 50 51 52 53 54
            $req->{sessionInfo}->{_facebookToken} = $access_token;

     # Get mandatory fields (see https://developers.facebook.com/tools/explorer)
            my @fields = ('id');

            # Look at wanted fields
Yadd's avatar
Yadd committed
55
            if ( $self->getModule( $req, 'user' ) =~ /^Facebook/ ) {
Yadd's avatar
Yadd committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
                push @fields,
                  map { /^(\w+)$/ ? ($1) : () }
                  values %{ $self->conf->{facebookExportedVars} };
            }
            my $datas;

            # When a field is not granted, Facebook returns only an error
            # without real explanation. So here we try to reduce query until
            # having a valid response
            while (@fields) {
                $datas = $fb->get(
                    'https://graph.facebook.com/me',
                    { fields => join( ',', @fields ) }
                )->as_hash;
                unless ( ref $datas ) {
Yadd's avatar
Yadd committed
71
                    $self->logger->error("Unable to get any Facebook field");
Yadd's avatar
Yadd committed
72 73 74 75
                    return PE_ERROR;
                }
                if ( $datas->{error} ) {
                    my $tmp = pop @fields;
Yadd's avatar
Yadd committed
76 77
                    $self->logger->warn(
"Unable to get some Facebook fields ($datas->{error}->{message}). Retrying without $tmp"
Yadd's avatar
Yadd committed
78 79 80 81 82 83 84
                    );
                }
                else {
                    last;
                }
            }
            unless (@fields) {
Yadd's avatar
Yadd committed
85
                $self->logger->error("Unable to get any Facebook field");
Yadd's avatar
Yadd committed
86 87 88
                return PE_ERROR;
            }

89 90
            # Use id field to trace user
            unless ( $datas->{id} ) {
Yadd's avatar
Yadd committed
91
                $self->logger->error('Unable to get Facebook id');
Yadd's avatar
Yadd committed
92 93
                return PE_ERROR;
            }
94
            $req->user( $datas->{id} );
Yadd's avatar
Yadd committed
95 96 97 98 99 100 101 102 103 104 105 106
            $req->datas->{_facebookDatas} = $datas;

            # Force redirection to avoid displaying Oauth datas
            $req->mustRedirect(1);
            return PE_OK;
        }
        return PE_BADCREDENTIALS;
    }

    # 1.2 Bad responses
    if ( my $error_code = $req->param('error_code') ) {
        my $error_message = $req->param('error_message');
Yadd's avatar
Tidy  
Yadd committed
107 108
        $self->userLogger->error(
            "Facebook error code $error_code: $error_message");
Yadd's avatar
Yadd committed
109 110 111 112 113 114 115 116 117 118 119 120
        return PE_ERROR;
    }

    # 2. Else redirect user to Facebook login page:

    # Build Facebook redirection
    # TODO: use a param to use "publish_stream" or not
    my $check_url = $fb->get_authorization_url(
        scope   => [ 'public_profile', 'email' ],
        display => 'page',
    );
    $req->urldc($check_url);
121 122
    $self->logger->debug( "Redirect user to " . $req->{urldc} );
    $req->continue(1);
Yadd's avatar
Yadd committed
123 124 125 126 127 128 129 130
    $req->steps( [] );
    PE_OK;
}

sub authenticate {
    PE_OK;
}

Yadd's avatar
Yadd committed
131 132
sub setAuthSessionInfo {
    my ( $self, $req ) = @_;
Yadd's avatar
Yadd committed
133 134
    $req->{sessionInfo}->{authenticationLevel} =
      $self->conf->{facebookAuthnLevel};
Yadd's avatar
Yadd committed
135 136 137
    PE_OK;
}

Yadd's avatar
Yadd committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
sub authFinish {
    PE_OK;
}

sub authLogout {
    PE_OK;
}

sub getDisplayType {
    return "logo";
}

sub fb {
    my ( $self, $req ) = @_;
    my $conf = $self->{conf};
    my $fb;
    my $sep = '?';
    my $ret = $conf->{portal};
    foreach my $v ( [ $req->datas->{_url}, "url" ],
        [ $req->param( $conf->{authChoiceParam} ), $conf->{authChoiceParam} ] )
    {
        if ( $v->[0] ) {
            $ret .= "$sep$v->[1]=$v->[0]";
            $sep = '&';
        }
    }

    eval {
        $fb = Net::Facebook::Oauth2->new(
            application_id     => $conf->{facebookAppId},
            application_secret => $conf->{facebookAppSecret},
            callback           => $ret,
        );
    };
Yadd's avatar
Yadd committed
172
    $self->logger->error($@) if ($@);
Yadd's avatar
Yadd committed
173 174 175 176
    return $fb;
}

1;