From 31c5a0088a56167b2a63268b44267ba388ef0ca5 Mon Sep 17 00:00:00 2001 From: Tomas Cohen Arazi Date: Wed, 10 May 2023 10:45:23 -0300 Subject: [PATCH] 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 --- Koha/REST/V1/OAuth/Client.pm | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Koha/REST/V1/OAuth/Client.pm b/Koha/REST/V1/OAuth/Client.pm index 0fb04089d7..b6f66b022c 100644 --- a/Koha/REST/V1/OAuth/Client.pm +++ b/Koha/REST/V1/OAuth/Client.pm @@ -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; -- 2.30.2