Do not follow redirects on authorization requests (fixes #195)
Change-Id: Idba831398cfa7c51fb8f56e55c5265234a0d4652
diff --git a/Changes b/Changes
index c0fab50..271d991 100755
--- a/Changes
+++ b/Changes
@@ -1,6 +1,8 @@
-0.49 2023-02-14
+0.49 2023-02-23
- Introduce conllu2korapxml command via plugin. (diewald)
- Introduce korapxml2conllu command via plugin. (diewald)
+ - Do not follow redirect links on OAuth authorization
+ requests. (diewald)
0.48 2023-01-12
- Added support for NKJP tagset in annotation
diff --git a/lib/Kalamar/Plugin/Auth.pm b/lib/Kalamar/Plugin/Auth.pm
index 5df0568..5fd3578 100644
--- a/lib/Kalamar/Plugin/Auth.pm
+++ b/lib/Kalamar/Plugin/Auth.pm
@@ -525,16 +525,23 @@
$app->helper(
korap_request => sub {
my $c = shift;
+
+ # Get plugin user agent
+ my $ua = $c->kalamar_ua;
+
+ # Override if UA is granted
+ if (ref $_[0] eq 'Mojo::UserAgent') {
+ $ua = shift;
+ };
+
my $method = shift;
my $path = shift;
+
my @param = @_;
# TODO:
# Check if $tx is not leaked!
- # Get plugin user agent
- my $ua = $c->kalamar_ua;
-
my $url = Mojo::URL->new($path);
my $tx = $ua->build_tx(uc($method), $url->clone, @param);
@@ -622,7 +629,7 @@
my $tx = shift;
# Response is fine
- if ($tx->res->is_success) {
+ if ($tx->res->is_success || $tx->res->is_redirect) {
return Mojo::Promise->resolve($tx);
}
@@ -1420,7 +1427,15 @@
state $r_url = Mojo::URL->new($c->korap->api)->path('oauth2/authorize');
$c->stash(redirect_uri => Mojo::URL->new($v->param('redirect_uri')));
- return $c->korap_request(post => $r_url, {} => form => {
+ my $ua = Mojo::UserAgent->new(
+ connect_timeout => 30,
+ inactivity_timeout => 30,
+ max_redirects => 0
+ );
+
+ $ua->server->app($app);
+
+ return $c->korap_request($ua, post => $r_url, { } => form => {
response_type => 'code',
client_id => $v->param('client_id'),
redirect_uri => $c->stash('redirect_uri'),
@@ -1436,6 +1451,20 @@
# Check for location header with code in redirects
my $loc;
+
+ # Look for code in location URL
+ if ($loc = $tx->res->headers->header('Location')) {
+ my $url = Mojo::URL->new($loc);
+
+ if ($url->query->param('code')) {
+ return Mojo::Promise->resolve($loc);
+ } elsif (my $err = $url->query->param('error_description')) {
+ return Mojo::Promise->reject($err);
+ }
+ };
+
+ # Check for location header with code in redirects
+ # This should be dead code tbh
foreach (@{$tx->redirects}) {
$loc = $_->res->headers->header('Location');
diff --git a/t/plugin/auth-oauth.t b/t/plugin/auth-oauth.t
index 9deec0e..dfe9fc0 100644
--- a/t/plugin/auth-oauth.t
+++ b/t/plugin/auth-oauth.t
@@ -1019,16 +1019,27 @@
->header_is('location', '/settings/oauth?error_description=Bad+CSRF+token')
;
+
+my $local_port = $t->get_ok('/')->tx->local_port;
+my $remote_port = $t->get_ok('/')->tx->remote_port;
+
+like($local_port, qr!^\d+$!);
+like($remote_port, qr!^\d+$!);
+
+my $port = $remote_port;
+
+my $redirect_url_fakeapi = $t->app->close_redirect_to(Mojo::URL->new('http://localhost:' . $port)->path($fake_backend_app->url_for('return_uri'))->to_abs->to_string);
+
$fwd = $t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
client_id => 'xyz',
state => 'abcde',
scope => 'search match',
- redirect_uri_server => 'http://example.com/',
- redirect_uri => $fake_backend_app->url_for('return_uri')->to_abs,
+ redirect_uri_server => 'http://localhost:'.$port,
+ redirect_uri => "$redirect_url_fakeapi",
csrf_token => $csrf,
}))
->status_is(302)
- ->header_like('location', qr!/realapi/fakeclient/return!)
+ ->header_like('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?code=.+$!)
->tx->res->headers->header('location')
;
@@ -1037,6 +1048,28 @@
->content_like(qr'welcome back! \[(.+?)\]')
;
+my $fake_port = $port;
+
+while ($fake_port == $remote_port || $fake_port == $local_port) {
+ $fake_port++;
+};
+
+$redirect_url_fakeapi = $t->app->close_redirect_to(Mojo::URL->new('http://localhost:' . $fake_port)->path($fake_backend_app->url_for('return_uri'))->to_abs->to_string);
+
+$fwd = $t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
+ client_id => 'xyz',
+ state => 'abcde',
+ scope => 'search match',
+ redirect_uri_server => 'http://localhost:'.$port,
+ redirect_uri => "$redirect_url_fakeapi",
+ csrf_token => $csrf,
+}))
+ ->status_is(302)
+ ->header_unlike('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?error_description=Connection\+refused$!)
+ ->header_like('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?code=.+?$!)
+ ->tx->res->headers->header('location')
+ ;
+
$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
client_id => 'xyz',
state => 'abcde',