diff --git a/app/Http/Controllers/RemoteAuthController.php b/app/Http/Controllers/RemoteAuthController.php index 4b889c933..00bfa397a 100644 --- a/app/Http/Controllers/RemoteAuthController.php +++ b/app/Http/Controllers/RemoteAuthController.php @@ -326,6 +326,7 @@ class RemoteAuthController extends Controller $token = $request->session()->get('oauth_remote_session_token'); $res = RemoteAuthService::getVerifyCredentials($domain, $token); + abort_if(! $res || ! isset($res['acct']), 403, 'Invalid credentials'); $res['_webfinger'] = strtolower('@'.$res['acct'].'@'.$domain); $res['_domain'] = strtolower($domain); $request->session()->put('oauth_remasto_id', $res['id']); diff --git a/app/Services/Account/RemoteAuthService.php b/app/Services/Account/RemoteAuthService.php index 3e59bf64e..bc9a98ed8 100644 --- a/app/Services/Account/RemoteAuthService.php +++ b/app/Services/Account/RemoteAuthService.php @@ -71,14 +71,23 @@ class RemoteAuthService } $url = 'https://'.$domain.'/oauth/token'; - $res = Http::asForm()->post($url, [ - 'code' => $code, - 'grant_type' => 'authorization_code', - 'client_id' => $raw->client_id, - 'client_secret' => $raw->client_secret, - 'redirect_uri' => $raw->redirect_uri, - 'scope' => 'read', - ]); + + try { + $res = Http::asForm()->timeout(20)->retry(3, 750)->post($url, [ + 'code' => $code, + 'grant_type' => 'authorization_code', + 'client_id' => $raw->client_id, + 'client_secret' => $raw->client_secret, + 'redirect_uri' => $raw->redirect_uri, + 'scope' => 'read', + ]); + } catch (RequestException $e) { + return false; + } catch (ConnectionException $e) { + return false; + } catch (\Exception $e) { + return false; + } return $res; } @@ -92,7 +101,18 @@ class RemoteAuthService $url = 'https://'.$domain.'/api/v1/accounts/verify_credentials'; - $res = Http::withToken($code)->get($url); + try { + $res = Http::withToken($code)->timeout(20)->retry(3, 750)->get($url); + if (! $res->ok()) { + return false; + } + } catch (RequestException $e) { + return false; + } catch (ConnectionException $e) { + return false; + } catch (\Exception $e) { + return false; + } return $res->json(); } @@ -108,7 +128,18 @@ class RemoteAuthService $key = self::CACHE_KEY.'get-following:code:'.substr($code, 0, 16).substr($code, -5).':domain:'.$domain.':id:'.$id; return Cache::remember($key, 3600, function () use ($url, $code) { - $res = Http::withToken($code)->get($url); + try { + $res = Http::withToken($code)->timeout(20)->retry(3, 750)->get($url); + if (! $res->ok()) { + return false; + } + } catch (RequestException $e) { + return false; + } catch (ConnectionException $e) { + return false; + } catch (\Exception $e) { + return false; + } return $res->json(); }); diff --git a/tests/Feature/RemoteAuthServiceTest.php b/tests/Feature/RemoteAuthServiceTest.php new file mode 100644 index 000000000..af67195d8 --- /dev/null +++ b/tests/Feature/RemoteAuthServiceTest.php @@ -0,0 +1,99 @@ + $domain, + 'client_id' => 'cid', + 'client_secret' => 'secret', + 'redirect_uri' => url('/auth/mastodon/callback'), + 'active' => true, + 'banned' => false, + ]); + } + + #[Test] + public function verify_credentials_returns_false_on_connection_failure() + { + $this->activeInstance(); + + Http::fake(function () { + throw new ConnectionException('timed out'); + }); + + $res = RemoteAuthService::getVerifyCredentials('mastodon.example', 'token'); + + $this->assertFalse($res); + } + + #[Test] + public function verify_credentials_returns_false_on_server_error() + { + $this->activeInstance(); + + Http::fake([ + '*' => Http::response('nope', 500), + ]); + + $res = RemoteAuthService::getVerifyCredentials('mastodon.example', 'token'); + + $this->assertFalse($res); + } + + #[Test] + public function verify_credentials_returns_json_on_success() + { + $this->activeInstance(); + + Http::fake([ + '*' => Http::response(['acct' => 'alice', 'id' => '1'], 200), + ]); + + $res = RemoteAuthService::getVerifyCredentials('mastodon.example', 'token'); + + $this->assertIsArray($res); + $this->assertSame('alice', $res['acct']); + } + + #[Test] + public function get_following_returns_false_on_connection_failure() + { + $this->activeInstance(); + + Http::fake(function () { + throw new ConnectionException('timed out'); + }); + + $res = RemoteAuthService::getFollowing('mastodon.example', 'token', 42); + + $this->assertFalse($res); + } + + #[Test] + public function get_token_returns_false_on_connection_failure() + { + $this->activeInstance(); + + Http::fake(function () { + throw new ConnectionException('timed out'); + }); + + $res = RemoteAuthService::getToken('mastodon.example', 'code'); + + $this->assertFalse($res); + } +}