Bug 33675: Add CSRF protection to OAuth/OIDC authentication

This patch makes the OAuth/OIDC client pass a `state` parameter with a
CSRF protection token, to be validated back when the flow returns to
Koha.

Ideally, the Mojolicious::Plugin::OAuth2 library should deal with this
implicitly, probably making use of JWT. But as of now, this is the best
way to implement it.

To test:
1. Have a working SSO solution (ktd --sso)
2. Click to login using SSO
=> SUCCESS: Notice a 'state' parameter on the URL, looks like a random
thing
3. When you login, no error is reported

Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
This commit is contained in:
Tomás Cohen Arazi 2023-05-10 10:45:23 -03:00
parent f436bc639c
commit 5d191f21e5
Signed by: tomascohen
GPG key ID: 0A272EA1B2F3C15F

View file

@ -24,6 +24,7 @@ use Mojo::URL;
use Scalar::Util qw(blessed);
use Try::Tiny;
use Koha::Logger;
use Koha::Token;
use URI::Escape qw(uri_escape_utf8);
=head1 NAME
@ -76,7 +77,32 @@ sub login {
$provider_config->{authorize_url} = $authorize_url->to_string;
}
return $c->oauth2->get_token_p( $provider, { redirect_uri => $redirect_url . $provider . "/" . $interface } )->then(
# 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 (
Koha::Token->new->check_csrf(
{
session_id => $c->req->cookie('CGISESSID')->value,
token => $c->param('state'),
}
)
)
{
my $error = "wrong_csrf_token";
return $c->redirect_to( $uri . "?auth_error=$error" );
}
}
else {
# initial request, generate CSRF token
$state = Koha::Token->new->generate_csrf( { session_id => $c->req->cookie('CGISESSID')->value } );
}
return $c->oauth2->get_token_p( $provider => { ( !$is_callback ? ( state => $state ) : () ), redirect_uri => $redirect_url . $provider . "/" . $interface } )->then(
sub {
return unless my $response = shift;