Ado

 view release on metacpan or  search on metacpan

lib/Ado/Plugin/Auth.pm  view on Meta::CPAN

        $c->debug(
            "in _login_facebook error from provider: " . ($c->param('error') || 'no error'));
    } if $Ado::Control::DEV_MODE;
    if ($response->{access_token}) {    #Athenticate, create and login the user.
        return _create_or_authenticate_facebook_user(
            $c,
            $response->{access_token},
            $providers->{$provider}
        );
    }
    else {
        #Redirect to front-page and say sorry
        # We are very sorry but we need to know you are a reasonable human being.
        $c->flash(error_login => $c->l('oauth2_sorry[_1]', ucfirst($provider))
              . ($c->param('error') || ''));
        $c->app->log->error('error_response:' . $c->dumper($response));
        $c->res->code(307);    #307 Temporary Redirect
        $c->redirect_to('/');
    }
    return;

}

sub _authenticate_oauth2_user {
    my ($c, $user, $time) = @_;
    if (   $user->disabled
        || ($user->stop_date != 0 && $user->stop_date < $time)
        || $user->start_date > $time)
    {
        $c->flash(login_message => $c->l('oauth2_disabled'));
        $c->redirect_to('/');
        return;
    }
    $c->session(login_name => $user->login_name);
    $c->user($user);
    $c->app->log->info('$user ' . $user->login_name . ' logged in!');
    return 1;
}

#Creates a user using given info from provider
sub _create_oauth2_user {
    my ($c, $user_info, $provider) = @_;
    state $app = $c->app;
    if (my $user = Ado::Model::Users->add(_user_info_to_args($user_info, $provider))) {
        $app->plugins->emit_hook(after_user_add => $c, $user, $user_info);
        $c->user($user);
        $c->session(login_name => $user->login_name);
        $app->log->info($user->description . ' New $user ' . $user->login_name . ' logged in!');
        $c->flash(login_message => $c->l('oauth2_wellcome[_1]', $user->name));
        $c->redirect_to('/');
        return 1;
    }
    $app->log->error($@);
    return;
}

#next two methods
#(_create_or_authenticate_facebook_user and _create_or_authenticate_google_user)
# exist only because we pass different parameters in the form
# which are specific to the provider.
# TODO: think of a way to map the generation of the form arguments to the
# specific provider so we can dramatically reduce the number of provider
# specific subroutines
sub _create_or_authenticate_facebook_user {
    my ($c, $access_token, $provider) = @_;
    my $ua = Mojo::UserAgent->new;
    my $appsecret_proof = Digest::SHA::hmac_sha256_hex($access_token, $provider->{secret});
    $c->debug('$appsecret_proof:' . $appsecret_proof);
    my $user_info =
      $ua->get($provider->{info_url},
        form => {access_token => $access_token, appsecret_proof => $appsecret_proof})->res->json;
    $c->debug('Response from info_url:' . $c->dumper($user_info)) if $Ado::Control::DEV_MODE;

    my $user = Ado::Model::Users->by_email($user_info->{email});
    my $time = time;

    if ($user->id) {
        return _authenticate_oauth2_user($c, $user, $time);
    }

    #else create the user
    return _create_oauth2_user($c, $user_info, $provider);
}

sub _create_or_authenticate_google_user {
    my ($c, $access_token, $provider) = @_;

    #make request for the user info
    my $token_type = 'Bearer';
    my $ua         = Mojo::UserAgent->new;
    my $user_info =
      $ua->get($provider->{info_url} => {Authorization => "$token_type $access_token"})
      ->res->json;

    my $user = Ado::Model::Users->by_email($user_info->{email});
    my $time = time;

    if ($user->id) {
        return _authenticate_oauth2_user($c, $user, $time);
    }

    #else create the user
    return _create_oauth2_user($c, $user_info, $provider);
}

# Redirects to Consent screen
sub authorize {
    my ($c)    = @_;
    my $m      = $c->param('auth_method');
    my $params = $c->app->config('Ado::Plugin::Auth')->{providers}{$m};
    $params->{redirect_uri} = '' . $c->url_for("/login/$m")->to_abs;

    #This call will redirect the user to the provider Consent screen.
    $c->redirect_to($c->oauth2->auth_url($m, %$params));
    return;
}

# Maps user info given from provider to arguments for
# Ado::Model::Users->new
sub _user_info_to_args {
    my ($ui, $provider) = @_;
    my %args;
    if (index($provider->{info_url}, 'google') > -1) {
        $args{first_name} = $ui->{given_name};
        $args{last_name}  = $ui->{family_name};
    }
    elsif (index($provider->{info_url}, 'facebook') > -1) {
        $args{first_name} = $ui->{first_name};
        $args{last_name}  = $ui->{last_name};
    }

    #Add another elsif to map different %args to $ui from a new provider
    else {
        Carp::croak('Unknown provider info_url:' . $provider->{info_url});
    }
    $args{email}      = $ui->{email};
    $args{login_name} = $ui->{email};
    $args{login_name} =~ s/[\@\.]+//g;
    $args{login_password} =
      Mojo::Util::sha1_hex($args{login_name} . Ado::Sessions->generate_id());
    $args{description} = "Registered via $provider->{info_url}!";
    $args{created_by}  = $args{changed_by} = 1;
    $args{start_date}  = $args{disabled} = $args{stop_date} = 0;

    return %args;
}
1;


=pod

=encoding utf8

=head1 NAME

Ado::Plugin::Auth - Passwordless user authentication for Ado

=head1 SYNOPSIS

  #in etc/ado.$mode.conf
  plugins =>[
    #...
    'auth',
    #...
  ],

    #in etc/plugins/auth.$mode.conf
    {
      #methods which will be displayed in the "Sign in" menu
      auth_methods => ['ado', 'facebook', 'google'],

      providers => {
        google => {
            key =>'123456789....apps.googleusercontent.com',
            secret =>'YourSECR3T',
            scope=>'profile email',
            info_url => 'https://www.googleapis.com/userinfo/v2/me',
        },
        facebook => {
            key =>'123456789',
            secret =>'123456789abcdef',
            scope =>'public_profile,email',
            info_url => 'https://graph.facebook.com/v2.2/me',
        },
      }
    }

=head1 DESCRIPTION

L<Ado::Plugin::Auth> is a plugin that authenticates users to an L<Ado> system.
Users can be authenticated via Google, Facebook, locally and in the future
other authentication service-providers.



( run in 0.626 second using v1.01-cache-2.11-cpan-39bf76dae61 )