Bug 42719: Handle missing CGISESSID in OAuth login (IdP-initiated flow)

When a user initiates login from the IdP (e.g. Okta app tile), they
hit the OAuth endpoint without an existing session cookie. The code
crashed calling ->value on an undefined cookie object.

This patch:
- Removes the premature 'No user session found' guard that blocked
  IdP-initiated flows entirely
- Creates a session on-the-fly for the initial request when no
  cookie exists (IdP-initiated flow)
- On callback, redirects with wrong_csrf_token error instead of
  crashing when the cookie is missing

CSRF protection is preserved: the state token is bound to the
session created in the initial request, and the browser returns
the cookie on callback for validation.

Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: David Cook <dcook@prosentient.com.au>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
Tomás Cohen Arazi 2026-08-19 09:31:38 -03:00 committed by Pedro Amorim
parent fda4343019
commit 9eaeae69e5
No known key found for this signature in database
GPG Key ID: 06B75BA7F3894D55
1 changed files with 17 additions and 10 deletions

View File

@ -66,10 +66,11 @@ sub login {
} }
my $current_session; my $current_session;
my $session_id;
my $current_session_cookie = $c->req->cookie('CGISESSID'); my $current_session_cookie = $c->req->cookie('CGISESSID');
if ($current_session_cookie) { if ($current_session_cookie) {
my $current_session_id = $current_session_cookie->value; $session_id = $current_session_cookie->value;
$current_session = Koha::Session->get_session( { sessionID => $current_session_id } ); $current_session = Koha::Session->get_session( { sessionID => $session_id } );
} }
unless ( $provider_config && $provider_config->{authorize_url} ) { unless ( $provider_config && $provider_config->{authorize_url} ) {
@ -77,11 +78,6 @@ sub login {
return $c->redirect_to( $uri . "?auth_error=$error" ); return $c->redirect_to( $uri . "?auth_error=$error" );
} }
unless ($current_session_cookie) {
my $error = "No user session found";
return $c->redirect_to( $uri . "?auth_error=$error" );
}
unless ( $provider_config->{authorize_url} =~ /response_type=code/ ) { unless ( $provider_config->{authorize_url} =~ /response_type=code/ ) {
my $authorize_url = Mojo::URL->new( $provider_config->{authorize_url} ); my $authorize_url = Mojo::URL->new( $provider_config->{authorize_url} );
$authorize_url->query->append( response_type => 'code' ); $authorize_url->query->append( response_type => 'code' );
@ -96,10 +92,15 @@ sub login {
if ($is_callback) { if ($is_callback) {
# callback, check CSRF token # callback, check CSRF token
unless ($session_id) {
my $error = "wrong_csrf_token";
return $c->redirect_to( $uri . "?auth_error=$error" );
}
unless ( unless (
Koha::Token->new->check_csrf( Koha::Token->new->check_csrf(
{ {
session_id => $current_session_cookie->value, session_id => $session_id,
token => $c->param('state'), token => $c->param('state'),
} }
) )
@ -110,8 +111,14 @@ sub login {
} }
} else { } else {
# initial request, generate CSRF token # initial request — create session if needed (IdP-initiated flow)
$state = Koha::Token->new->generate_csrf( { session_id => $current_session_cookie->value } ); 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( Koha::Auth::Identity::Referer->store_referer(
{ {