PEF-Front
view release on metacpan or search on metacpan
lib/PEF/Front/Oauth.pm view on Meta::CPAN
package PEF::Front::Oauth;
use strict;
use warnings;
use URI;
use LWP::UserAgent;
use HTTP::Request::Common;
use JSON;
use PEF::Front::Config;
use PEF::Front::Session;
my $coro_ae_lwp;
BEGIN {
if ($INC{'Coro/AnyEvent.pm'}) {
eval "use AnyEvent::HTTP::LWP::UserAgent";
$coro_ae_lwp = ($@) ? 0 : 1;
} else {
$coro_ae_lwp = 0;
}
}
sub _authorization_server {
die 'unimplemented base method';
}
sub _token_request {
die 'unimplemented base method';
}
sub _get_user_info_request {
die 'unimplemented base method';
}
sub _parse_user_info {
die 'unimplemented base method';
}
sub _required_redirect_uri {0}
sub _required_state {1}
sub _returns_state {1}
sub _decode_token {
decode_json($_[1]);
}
sub user_info_scope {
my ($self) = @_;
cfg_oauth_scopes($self->{service})->{user_info};
}
sub authorization_server {
my ($self, $scope, $redirect_uri) = @_;
my $uri = URI->new($self->_authorization_server);
$self->{state} = PEF::Front::Session::_secure_value;
$self->{session}->data->{oauth_state}{$self->{state}} = $self->{service};
my @extra = ();
if (defined $scope) {
@extra = (scope => $scope);
}
if (defined $redirect_uri) {
my $uri = URI->new($redirect_uri);
$uri->query_form($uri->query_form, state => $self->{state}) unless $self->_returns_state;
push @extra, (redirect_uri => $uri->as_string);
$self->{session}->data->{oauth_redirect_uri}{$self->{service}} = $uri->as_string;
} elsif ($self->_required_redirect_uri) {
die {
result => 'OAUTHERR',
answer => 'Oauth $1 requires redirect_uri',
answer_args => [$self->{service}]
};
}
push @extra, (state => $self->{state}) if $self->_required_state;
$uri->query_form(
response_type => 'code',
client_id => cfg_oauth_client_id($self->{service}),
@extra
);
$uri->as_string;
}
sub exchange_code_to_token {
my ($self, $request) = @_;
if ($request->{code}) {
my $token_answer;
delete $self->{session}->data->{oauth_state};
$self->{session}->store;
my $exception;
if ($coro_ae_lwp && $Coro::main != $Coro::current) {
my $lwp_user_agent = AnyEvent::HTTP::LWP::UserAgent->new;
$lwp_user_agent->timeout(cfg_oauth_connect_timeout());
my $request = $self->_token_request($request->{code});
my $response = $lwp_user_agent->request($request);
$exception = "timeout" if !$response or !$response->decoded_content;
} else {
eval {
local $SIG{ALRM} = sub {die "timeout"};
alarm cfg_oauth_connect_timeout();
my $lwp_user_agent = LWP::UserAgent->new;
my $request = $self->_token_request($request->{code});
my $response = $lwp_user_agent->request($request);
die if !$response or !$response->decoded_content;
$token_answer = $self->_decode_token($response->decoded_content);
};
$exception = $@;
alarm 0;
}
delete $self->{session}->data->{oauth_redirect_uri}{$self->{service}};
if ($exception) {
$self->{session}->data->{oauth_error} = $exception;
die {
result => 'OAUTHERR',
answer => 'Oauth timeout'
} if $exception =~ /timeout/;
die {
result => 'OAUTHERR',
answer => 'Oauth connect error'
};
}
if ($token_answer->{error} || !$token_answer->{access_token}) {
$self->{session}->data->{oauth_error}
= $token_answer->{error_description} || $token_answer->{error} || 'no access token';
die {
result => 'OAUTHERR',
answer => 'Oauth error: $1',
answer_args => [$self->{session}->data->{oauth_error}]
};
}
$self->{session}->load;
delete $self->{session}->data->{oauth_error};
$self->{session}->data->{oauth_access_token}{$self->{service}} = $token_answer->{access_token};
$self->{session}->store;
} else {
my $message = $request->{error_description} || $request->{error} || 'Internal Oauth error';
die {
result => 'OAUTHERR',
answer => $message
};
}
}
sub get_user_info {
my ($self) = @_;
my $info;
$self->{session}->store;
my $exception;
if ($coro_ae_lwp && $Coro::main != $Coro::current) {
my $lwp_user_agent = AnyEvent::HTTP::LWP::UserAgent->new;
$lwp_user_agent->timeout(cfg_oauth_connect_timeout());
my $response = $lwp_user_agent->request($self->_get_user_info_request);
if ($response && $response->decoded_content) {
$info = eval {decode_json $response->decoded_content};
$exception = $@;
} else {
$exception = "timeout";
}
} else {
eval {
local $SIG{ALRM} = sub {die "timeout"};
alarm cfg_oauth_connect_timeout();
my $lwp_user_agent = LWP::UserAgent->new;
my $response = $lwp_user_agent->request($self->_get_user_info_request);
die if !$response or !$response->decoded_content;
$info = decode_json $response->decoded_content;
};
$exception = $@;
alarm 0;
}
if ($exception) {
$self->{session}->data->{oauth_error} = $exception;
die {
result => 'OAUTHERR',
answer => 'Oauth timeout'
} if $exception =~ /timeout/;
die {
result => 'OAUTHERR',
answer => 'Oauth connect error'
};
}
if ($info->{error}) {
$self->{session}->data->{oauth_error} = $info->{error_description} || $info->{error};
die {
result => 'OAUTHERR',
answer => 'Oauth error: $1',
answer_args => [$self->{session}->data->{oauth_error}]
};
}
$self->{session}->load;
delete $self->{session}->data->{oauth_error};
$self->{session}->data->{oauth_info_raw}{$self->{service}} = $info;
$self->{session}->data->{oauth_info} = [] if !$self->{session}->data->{oauth_info};
my $oi = $self->{session}->data->{oauth_info};
for (my $i = 0; $i < @$oi; ++$i) {
if ($oi->[$i]->{service} eq $self->{service}) {
splice @$oi, $i, 1;
last;
}
}
my $parsed_info = $self->_parse_user_info;
$parsed_info->{service} = $self->{service};
unshift @$oi, $parsed_info;
$self->{session}->store;
$parsed_info;
}
sub load_module {
my ($auth_service) = @_;
( run in 0.476 second using v1.01-cache-2.11-cpan-d7f47b0818f )