From fc479c7650e38cd3cfe509014943ffb471ce76c8 Mon Sep 17 00:00:00 2001 From: Dane Powell Date: Tue, 4 Jan 2022 15:47:28 -0800 Subject: [PATCH 1/2] Fix #10366: Improve messaging when GitHub tokens need SSO authorization --- src/Composer/Util/AuthHelper.php | 16 +++++++++++++ src/Composer/Util/GitHub.php | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/Composer/Util/AuthHelper.php b/src/Composer/Util/AuthHelper.php index daac4943904b..64ccf60f71b7 100644 --- a/src/Composer/Util/AuthHelper.php +++ b/src/Composer/Util/AuthHelper.php @@ -92,6 +92,22 @@ public function promptAuthIfNeeded($url, $origin, $statusCode, $reason = null, $ $message = "\n"; $rateLimited = $gitHubUtil->isRateLimited($headers); + $requiresSso = $gitHubUtil->requiresSso($headers); + + if ($requiresSso) { + $ssoUrl = $gitHubUtil->getSsoUrl($headers); + $message = sprintf( + 'GitHub API token requires SSO authorization. Authorize this token at ' . $ssoUrl, + $ssoUrl + ) . "\n"; + $this->io->writeError($message); + if (!$this->io->isInteractive()) { + throw new TransportException('Could not authenticate against ' . $origin, 403); + } + $this->io->ask('After authorizing your token, confirm that you would like to retry the request'); + return ['retry' => TRUE, 'storeAuth' => $storeAuth]; + } + if ($rateLimited) { $rateLimit = $gitHubUtil->getRateLimit($headers); if ($this->io->hasAuthentication($origin)) { diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index 1c5b9a2aa44b..49db10a360d0 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -171,6 +171,27 @@ public function getRateLimit(array $headers) return $rateLimit; } + /** + * Extract SSO URL from response. + * + * @param string[] $headers Headers from Composer\Downloader\TransportException. + * + * @return string|null + */ + public function getSsoUrl(array $headers) + { + foreach ($headers as $header) { + $header = trim($header); + if (false === strpos($header, 'x-github-sso: required')) { + continue; + } + list(, $url) = explode('=', $header, 2); + return $url; + } + + return null; + } + /** * Finds whether a request failed due to rate limiting * @@ -188,4 +209,24 @@ public function isRateLimited(array $headers) return false; } + + /** + * Finds whether a request failed due to lacking SSO authorization + * + * @see https://docs.github.com/en/rest/overview/other-authentication-methods#authenticating-for-saml-sso + * + * @param string[] $headers Headers from Composer\Downloader\TransportException. + * + * @return bool + */ + public function requiresSso(array $headers) + { + foreach ($headers as $header) { + if (Preg::isMatch('{^X-GitHub-SSO: required}i', trim($header))) { + return true; + } + } + + return false; + } } From 2644a11d36d8437c9da62d376bf82d096fd253a8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 8 Jan 2022 12:00:06 +0100 Subject: [PATCH 2/2] Make URL extraction more robust, add php 5.3 support --- src/Composer/Util/AuthHelper.php | 3 ++- src/Composer/Util/GitHub.php | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Composer/Util/AuthHelper.php b/src/Composer/Util/AuthHelper.php index 64ccf60f71b7..f49373397913 100644 --- a/src/Composer/Util/AuthHelper.php +++ b/src/Composer/Util/AuthHelper.php @@ -105,7 +105,8 @@ public function promptAuthIfNeeded($url, $origin, $statusCode, $reason = null, $ throw new TransportException('Could not authenticate against ' . $origin, 403); } $this->io->ask('After authorizing your token, confirm that you would like to retry the request'); - return ['retry' => TRUE, 'storeAuth' => $storeAuth]; + + return array('retry' => true, 'storeAuth' => $storeAuth); } if ($rateLimited) { diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index 49db10a360d0..2dd53e3506c6 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -182,11 +182,12 @@ public function getSsoUrl(array $headers) { foreach ($headers as $header) { $header = trim($header); - if (false === strpos($header, 'x-github-sso: required')) { + if (false === stripos($header, 'x-github-sso: required')) { continue; } - list(, $url) = explode('=', $header, 2); - return $url; + if (Preg::isMatch('{\burl=(?P[^\s;]+)}', $header, $match)) { + return $match['url']; + } } return null;