Koha::REST::V1::Auth checks the request URL path and do various things depending on how it looks. For instance, it allows everyone to access paths starting with "/api/v1/oauth/" But because of how Koha::REST::V1 was written, when using mojolicious applications Koha::App::Intranet and Koha::App::Opac, paths had to add a path prefix ("/api"), which means the final path as seen by Koha::REST::V1::Auth looked like this: "/api/api/v1/oauth" I said "had to", but actually there is another way that does not require this path manipulation and that's what this bug is about
Created attachment 168644 [details] [review] Bug 37286: Fix REST API authentication when using Mojo apps Koha::REST::V1::Auth checks the request URL path and do various things depending on how it looks. For instance, it allows everyone to access paths starting with "/api/v1/oauth/" But because of how Koha::REST::V1 was written, when using mojolicious applications Koha::App::Intranet and Koha::App::Opac, paths had to add a path prefix ("/api"), which means the final path as seen by Koha::REST::V1::Auth looked like this: "/api/api/v1/oauth". I said "had to", but actually there is another way that does not require this path manipulation and that's what this patch does: Koha::REST::V1 now accepts a configuration parameter that allows to change the base path for API routes, which allows Koha::App::Plugin::RESTV1 (used by Koha::App::Intranet and Koha::App::Opac) to generate the right routes. This configuration parameter defaults to "/api/v1", so when outside of Koha::App::Intranet and Koha::App::Opac (when using debian/templates/plack.psgi for instance), the behavior is unchanged. Test plan: 1. Do not apply the patch yet 2. Run `bin/intranet daemon -l http://*:8080` 3. Run `curl -i -d{} http://127.0.0.1:8080/api/v1/oauth/token` It should return a 403 error, with an error message "Authentication failure". 4. Stop `bin/intranet daemon -l http://*:8080` by hitting Ctrl-C on the terminal you started it 5. Apply the patch 6. Run `bin/intranet daemon -l http://*:8080` again 7. Run `curl -i -d{} http://127.0.0.1:8080/api/v1/oauth/token` This time it should return a 400 error with an error message saying the "grant_type" property is missing. This error is normal as we did not send any data in the POST request body, and seeing this means Koha allowed us to use that route because it recognized '/api/v1/oauth/' at the start of the URL path 8. You can do the same test with `bin/opac`
Any hints on how to set things up for testing this? In koha-testing-docker, I get this when running step 2 of the test plan: bin/intranet daemon -l http://*:8080 Can't create listen socket: Address already in use at /usr/share/perl5/Mojo/IOLoop.pm line 124 PS I don't know what a "Mojo app" is, so maybe I'm not the right person to test this bug 8-)
Created attachment 176544 [details] [review] Bug 37286: Fix REST API authentication when using Mojo apps Koha::REST::V1::Auth checks the request URL path and do various things depending on how it looks. For instance, it allows everyone to access paths starting with "/api/v1/oauth/" But because of how Koha::REST::V1 was written, when using mojolicious applications Koha::App::Intranet and Koha::App::Opac, paths had to add a path prefix ("/api"), which means the final path as seen by Koha::REST::V1::Auth looked like this: "/api/api/v1/oauth". I said "had to", but actually there is another way that does not require this path manipulation and that's what this patch does: Koha::REST::V1 now accepts a configuration parameter that allows to change the base path for API routes, which allows Koha::App::Plugin::RESTV1 (used by Koha::App::Intranet and Koha::App::Opac) to generate the right routes. This configuration parameter defaults to "/api/v1", so when outside of Koha::App::Intranet and Koha::App::Opac (when using debian/templates/plack.psgi for instance), the behavior is unchanged. Test plan: 1. Do not apply the patch yet 2. Run `bin/intranet daemon -l http://*:8080` 3. Run `curl -i -d{} http://127.0.0.1:8080/api/v1/oauth/token` It should return a 403 error, with an error message "Authentication failure". 4. Stop `bin/intranet daemon -l http://*:8080` by hitting Ctrl-C on the terminal you started it 5. Apply the patch 6. Run `bin/intranet daemon -l http://*:8080` again 7. Run `curl -i -d{} http://127.0.0.1:8080/api/v1/oauth/token` This time it should return a 400 error with an error message saying the "grant_type" property is missing. This error is normal as we did not send any data in the POST request body, and seeing this means Koha allowed us to use that route because it recognized '/api/v1/oauth/' at the start of the URL path 8. You can do the same test with `bin/opac` Signed-off-by: Magnus Enger <magnus@libriotech.no> Works as advertised. See Bugzilla for notes from testing.
Initially, I had the same problem as David (in KTD), but I got around it by replacing :8080 with :8083 in the test plan. Here is what I got: === Server === $ bin/intranet daemon -l http://*:8083 [2025-01-15 10:28:23.65073] [11469] [info] Listening at "http://*:8083" Web application available at http://127.0.0.1:8083 === Client === $ curl -i -d{} http://127.0.0.1:8083/api/v1/oauth/token HTTP/1.1 401 Unauthorized Server: Mojolicious (Perl) Date: Wed, 15 Jan 2025 10:29:10 GMT Content-Type: application/json; charset=utf8 Content-Length: 35 {"error":"Authentication failure."} === Server === [2025-01-15 10:29:10.88254] [11469] [trace] [smrnH4W71gxh] POST "/api/api/v1/oauth/token" [2025-01-15 10:29:10.88285] [11469] [trace] [smrnH4W71gxh] Routing to application "Koha::REST::V1" [2025-01-15 10:29:10.88386] [11469] [trace] [smrnH4W71gxh] POST "/api/api/v1/oauth/token" [2025-01-15 10:29:10.88987] [11469] [trace] [smrnH4W71gxh] Routing to controller "Koha::REST::V1::Auth" and action "under" [2025-01-15 10:29:10.89101] [11469] [trace] [smrnH4W71gxh] 401 Unauthorized (0.007137s, 140.115/s) Applied the patch and restarted all the things. === Server === $ bin/intranet daemon -l http://*:8083 [2025-01-15 10:30:57.89162] [12106] [info] Listening at "http://*:8083" Web application available at http://127.0.0.1:8083 === Client === $ curl -i -d{} http://127.0.0.1:8083/api/v1/oauth/token HTTP/1.1 400 Bad Request Server: Mojolicious (Perl) Content-Length: 79 Content-Type: application/json;charset=UTF-8 Date: Wed, 15 Jan 2025 10:31:04 GMT {"errors":[{"message":"Missing property.","path":"\/grant_type"}],"status":400} === Server === [2025-01-15 10:31:04.33156] [12106] [trace] [QQTeoJ7i-Vbe] POST "/api/v1/oauth/token" [2025-01-15 10:31:04.33186] [12106] [trace] [QQTeoJ7i-Vbe] Routing to application "Koha::REST::V1" [2025-01-15 10:31:04.33434] [12106] [trace] [QQTeoJ7i-Vbe] POST "/api/v1/oauth/token" [2025-01-15 10:31:04.34088] [12106] [trace] [QQTeoJ7i-Vbe] Routing to controller "Koha::REST::V1::Auth" and action "under" [2025-01-15 10:31:04.34195] [12106] [trace] [QQTeoJ7i-Vbe] Routing to controller "Koha::REST::V1::OAuth" and action "token" [2025-01-15 10:31:04.34253] [12106] [trace] [QQTeoJ7i-Vbe] 400 Bad Request (0.008181s, 122.234/s) So I have verified this works according to the tese plan, but it will definitely need a more thorough review of the code. :-)