Koha-clms/Koha/REST/V1/OAuth/Client.pm

204 lines
6.4 KiB
Perl

package Koha::REST::V1::OAuth::Client;
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha 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 Koha; if not, see <https://www.gnu.org/licenses>.
use Modern::Perl;
use Koha::Auth::Client::OAuth;
use Mojo::Base 'Mojolicious::Controller';
use Mojo::URL;
use Scalar::Util qw(blessed);
use Try::Tiny;
use Koha::Token;
use URI::Escape qw(uri_escape_utf8);
use Koha::Session;
use Koha::Auth::Identity::Referer;
=head1 NAME
Koha::REST::V1::OAuth::Client - Controller library for handling OAuth2-related login attempts
=head1 API
=head2 Methods
=head3 login
Controller method handling login requests
=cut
sub login {
my $c = shift->openapi->valid_input or return;
my $provider = $c->param('provider_code');
my $interface = $c->param('interface');
my $provider_config = $c->oauth2->providers->{$provider};
my $uri;
my $redirect_url;
if ( $interface eq 'opac' ) {
$redirect_url = C4::Context->preference('OPACBaseURL') . '/api/v1/public/oauth/login/';
if ( C4::Context->preference('OpacPublic') ) {
$uri = '/cgi-bin/koha/opac-user.pl';
} else {
$uri = '/cgi-bin/koha/opac-main.pl';
}
} else {
$redirect_url = C4::Context->preference('staffClientBaseURL') . '/api/v1/oauth/login/';
$uri = '/cgi-bin/koha/mainpage.pl';
}
my $current_session;
my $session_id;
my $current_session_cookie = $c->req->cookie('CGISESSID');
if ($current_session_cookie) {
$session_id = $current_session_cookie->value;
$current_session = Koha::Session->get_session( { sessionID => $session_id } );
}
unless ( $provider_config && $provider_config->{authorize_url} ) {
my $error = "No configuration found for your provider";
return $c->redirect_to( $uri . "?auth_error=$error" );
}
unless ( $provider_config->{authorize_url} =~ /response_type=code/ ) {
my $authorize_url = Mojo::URL->new( $provider_config->{authorize_url} );
$authorize_url->query->append( response_type => 'code' );
$provider_config->{authorize_url} = $authorize_url->to_string;
}
# Determine if it is a callback request, or the initial
my $is_callback = $c->param('error_description') || $c->param('error') || $c->param('code');
my $state;
if ($is_callback) {
# callback, check CSRF token
unless ($session_id) {
my $error = "wrong_csrf_token";
return $c->redirect_to( $uri . "?auth_error=$error" );
}
unless (
Koha::Token->new->check_csrf(
{
session_id => $session_id,
token => $c->param('state'),
}
)
)
{
my $error = "wrong_csrf_token";
return $c->redirect_to( $uri . "?auth_error=$error" );
}
} else {
# initial request — create session if needed (IdP-initiated flow)
unless ($session_id) {
$current_session = Koha::Session->get_session( {} );
$session_id = $current_session->id;
$c->cookie( CGISESSID => $session_id, { path => '/' } );
}
$state = Koha::Token->new->generate_csrf( { session_id => $session_id } );
Koha::Auth::Identity::Referer->store_referer(
{
referer => $c->req->headers->referer,
interface => $interface,
session => $current_session,
}
);
}
return $c->oauth2->get_token_p(
$provider => {
( !$is_callback ? ( state => $state ) : () ), redirect_uri => $redirect_url . $provider . "/" . $interface
}
)->then(
sub {
return unless my $response = shift;
try {
my ( $patron, $mapped_data, $domain ) = Koha::Auth::Client::OAuth->new->get_user(
{
provider => $provider,
data => $response,
interface => $interface,
config => $c->oauth2->providers->{$provider}
}
);
if ( !$patron ) {
$patron = $c->auth->register(
{
data => $mapped_data,
domain => $domain,
interface => $interface
}
);
}
my $session_id =
$c->auth->session( { patron => $patron, interface => $interface, provider => $provider } );
$c->cookie( CGISESSID => $session_id, { path => "/" } );
my $target_uri = Koha::Auth::Identity::Referer->get_referer(
{
session => $current_session,
}
);
if ($target_uri) {
$uri = $target_uri;
}
$c->redirect_to($uri);
} catch {
my $error = $_;
$c->app->log->error($error);
# TODO: Review behavior
if ( blessed $error ) {
if ( $error->isa('Koha::Exceptions::Auth::Unauthorized') ) {
$error = "User cannot access this resource";
}
if ( $error->isa('Koha::Exceptions::Auth::NoValidDomain') ) {
$error = "No configuration found for your email domain";
}
}
$error = uri_escape_utf8($error);
$c->redirect_to( $uri . "?auth_error=$error" );
};
}
)->catch(
sub {
my $error = shift;
$c->app->log->error($error);
$error = uri_escape_utf8($error);
$c->redirect_to( $uri . "?auth_error=$error" );
}
)->wait;
}
1;