Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. AuthHelper.php

function AuthHelper::promptAuthIfNeeded

@phpstan-return array{retry: bool, storeAuth: 'prompt'|bool}

Parameters

int $statusCode HTTP status code that triggered this call:

string|null $reason a message/description explaining why this was called:

string[] $headers:

int $retryCount the amount of retries already done on this URL:

Return value

array containing retry (bool) and storeAuth (string|bool) keys, if retry is true the request should be retried, if storeAuth is true then on a successful retry the authentication should be persisted to auth.json

File

vendor/composer/composer/src/Composer/Util/AuthHelper.php, line 84

Class

AuthHelper
@author Jordi Boggiano <j.boggiano@seld.be>

Namespace

Composer\Util

Code

public function promptAuthIfNeeded(string $url, string $origin, int $statusCode, ?string $reason = null, array $headers = [], int $retryCount = 0) : array {
    $storeAuth = false;
    if (in_array($origin, $this->config
        ->get('github-domains'), true)) {
        $gitHubUtil = new GitHub($this->io, $this->config, null);
        $message = "\n";
        $rateLimited = $gitHubUtil->isRateLimited($headers);
        $requiresSso = $gitHubUtil->requiresSso($headers);
        if ($requiresSso) {
            $ssoUrl = $gitHubUtil->getSsoUrl($headers);
            $message = 'GitHub API token requires SSO authorization. Authorize this token at ' . $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)) {
                $message = 'Review your configured GitHub OAuth token or enter a new one to go over the API rate limit.';
            }
            else {
                $message = 'Create a GitHub OAuth token to go over the API rate limit.';
            }
            $message = sprintf('GitHub API limit (%d calls/hr) is exhausted, could not fetch ' . $url . '. ' . $message . ' You can also wait until %s for the rate limit to reset.', $rateLimit['limit'], $rateLimit['reset']) . "\n";
        }
        else {
            $message .= 'Could not fetch ' . $url . ', please ';
            if ($this->io
                ->hasAuthentication($origin)) {
                $message .= 'review your configured GitHub OAuth token or enter a new one to access private repos';
            }
            else {
                $message .= 'create a GitHub OAuth token to access private repos';
            }
        }
        if (!$gitHubUtil->authorizeOAuth($origin) && (!$this->io
            ->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($origin, $message))) {
            throw new TransportException('Could not authenticate against ' . $origin, 401);
        }
    }
    elseif (in_array($origin, $this->config
        ->get('gitlab-domains'), true)) {
        $message = "\n" . 'Could not fetch ' . $url . ', enter your ' . $origin . ' credentials ' . ($statusCode === 401 ? 'to access private repos' : 'to go over the API rate limit');
        $gitLabUtil = new GitLab($this->io, $this->config, null);
        $auth = null;
        if ($this->io
            ->hasAuthentication($origin)) {
            $auth = $this->io
                ->getAuthentication($origin);
            if (in_array($auth['password'], [
                'gitlab-ci-token',
                'private-token',
                'oauth2',
            ], true)) {
                throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode);
            }
        }
        if (!$gitLabUtil->authorizeOAuth($origin) && (!$this->io
            ->isInteractive() || !$gitLabUtil->authorizeOAuthInteractively(parse_url($url, PHP_URL_SCHEME), $origin, $message))) {
            throw new TransportException('Could not authenticate against ' . $origin, 401);
        }
        if ($auth !== null && $this->io
            ->hasAuthentication($origin)) {
            if ($auth === $this->io
                ->getAuthentication($origin)) {
                throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode);
            }
        }
    }
    elseif ($origin === 'bitbucket.org' || $origin === 'api.bitbucket.org') {
        $askForOAuthToken = true;
        $origin = 'bitbucket.org';
        if ($this->io
            ->hasAuthentication($origin)) {
            $auth = $this->io
                ->getAuthentication($origin);
            if ($auth['username'] !== 'x-token-auth') {
                $bitbucketUtil = new Bitbucket($this->io, $this->config);
                $accessToken = $bitbucketUtil->requestToken($origin, $auth['username'], $auth['password']);
                if (!empty($accessToken)) {
                    $this->io
                        ->setAuthentication($origin, 'x-token-auth', $accessToken);
                    $askForOAuthToken = false;
                }
            }
            elseif (!isset($this->bitbucketRetry[$url])) {
                // when multiple requests fire at the same time, they will all fail and the first one resets the token to be correct above but then the others
                // reach the code path and without this fallback they would end up throwing below
                // see https://github.com/composer/composer/pull/11464 for more details
                $askForOAuthToken = false;
                $this->bitbucketRetry[$url] = true;
            }
            else {
                throw new TransportException('Could not authenticate against ' . $origin, 401);
            }
        }
        if ($askForOAuthToken) {
            $message = "\n" . 'Could not fetch ' . $url . ', please create a bitbucket OAuth token to ' . ($statusCode === 401 || $statusCode === 403 ? 'access private repos' : 'go over the API rate limit');
            $bitBucketUtil = new Bitbucket($this->io, $this->config);
            if (!$bitBucketUtil->authorizeOAuth($origin) && (!$this->io
                ->isInteractive() || !$bitBucketUtil->authorizeOAuthInteractively($origin, $message))) {
                throw new TransportException('Could not authenticate against ' . $origin, 401);
            }
        }
    }
    else {
        // 404s are only handled for github
        if ($statusCode === 404) {
            return [
                'retry' => false,
                'storeAuth' => false,
            ];
        }
        // fail if the console is not interactive
        if (!$this->io
            ->isInteractive()) {
            if ($statusCode === 401) {
                $message = "The '" . $url . "' URL required authentication (HTTP 401).\nYou must be using the interactive console to authenticate";
            }
            elseif ($statusCode === 403) {
                $message = "The '" . $url . "' URL could not be accessed (HTTP 403): " . $reason;
            }
            else {
                $message = "Unknown error code '" . $statusCode . "', reason: " . $reason;
            }
            throw new TransportException($message, $statusCode);
        }
        // fail if we already have auth
        if ($this->io
            ->hasAuthentication($origin)) {
            // if two or more requests are started together for the same host, and the first
            // received authentication already, we let the others retry before failing them
            if ($retryCount === 0) {
                return [
                    'retry' => true,
                    'storeAuth' => false,
                ];
            }
            throw new TransportException("Invalid credentials (HTTP {$statusCode}) for '{$url}', aborting.", $statusCode);
        }
        $this->io
            ->writeError('    Authentication required (<info>' . $origin . '</info>):');
        $username = $this->io
            ->ask('      Username: ');
        $password = $this->io
            ->askAndHideAnswer('      Password: ');
        $this->io
            ->setAuthentication($origin, $username, $password);
        $storeAuth = $this->config
            ->get('store-auths');
    }
    return [
        'retry' => true,
        'storeAuth' => $storeAuth,
    ];
}

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal