diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b526cfa5..e5c61cc77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +### [2.2.4] 2022-01-08 + + * Fixed handling of process timeout when running async processes during installation + * Fixed GitLab API handling when projects have a repository disabled (#10440) + * Fixed reading of environment variables (e.g. APPDATA) containing unicode characters to workaround a PHP bug on Windows (#10434) + * Fixed partial update issues with path repos missing if a path repo is required by a path repo (#10431) + * Fixed support for sourcing binaries via the new bin proxies ([#10389](https://github.com/composer/composer/issues/10389#issuecomment-1007372740)) + * Fixed messaging when GitHub tokens need SSO authorization (#10432) + ### [2.2.3] 2021-12-31 * Fixed issue with PHPUnit and process isolation now including PHPUnit <6.5 (#10387) @@ -1358,6 +1367,7 @@ * Initial release +[2.2.4]: https://github.com/composer/composer/compare/2.2.3...2.2.4 [2.2.3]: https://github.com/composer/composer/compare/2.2.2...2.2.3 [2.2.2]: https://github.com/composer/composer/compare/2.2.1...2.2.2 [2.2.1]: https://github.com/composer/composer/compare/2.2.0...2.2.1 diff --git a/src/Composer/Util/AuthHelper.php b/src/Composer/Util/AuthHelper.php index daac49439..f49373397 100644 --- a/src/Composer/Util/AuthHelper.php +++ b/src/Composer/Util/AuthHelper.php @@ -92,6 +92,23 @@ class AuthHelper $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 array('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 1c5b9a2aa..2dd53e350 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -171,6 +171,28 @@ class GitHub 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 === stripos($header, 'x-github-sso: required')) { + continue; + } + if (Preg::isMatch('{\burl=(?P[^\s;]+)}', $header, $match)) { + return $match['url']; + } + } + + return null; + } + /** * Finds whether a request failed due to rate limiting * @@ -188,4 +210,24 @@ class GitHub 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; + } }