docker/docker_client.go
func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope) error {
	if len(c.challenges) == 0 {
		return nil
	}
	schemeNames := make([]string, 0, len(c.challenges))
	for _, challenge := range c.challenges {
		schemeNames = append(schemeNames, challenge.Scheme)
		switch challenge.Scheme {
		case "basic":
			req.SetBasicAuth(c.username, c.password)
			return nil
		case "bearer":
			cacheKey := ""
			scopes := []authScope{c.scope}
			if extraScope != nil {
				// Using ':' as a separator here is unambiguous because getBearerToken below uses the same separator when formatting a remote request (and because repository names can't contain colons).
				cacheKey = fmt.Sprintf("%s:%s", extraScope.remoteName, extraScope.actions)
				scopes = append(scopes, *extraScope)
			}
			var token bearerToken
			t, inCache := c.tokenCache.Load(cacheKey)
			if inCache {
				token = t.(bearerToken)
			}
			if !inCache || time.Now().After(token.expirationTime) {
				t, err := c.getBearerToken(req.Context(), challenge, scopes)
				if err != nil {
					return err
				}
				token = *t
				c.tokenCache.Store(cacheKey, token)
			}
			req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.Token))
			return nil
		default:
			logrus.Debugf("no handler for %s authentication", challenge.Scheme)
		}
	}
	logrus.Infof("None of the challenges sent by server (%s) are supported, trying an unauthenticated request anyway", strings.Join(schemeNames, ", "))
	return nil
}
func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope) error {
	if len(c.challenges) == 0 {
		return nil
	}
	schemeNames := make([]string, 0, len(c.challenges))
	for _, challenge := range c.challenges {
		schemeNames = append(schemeNames, challenge.Scheme)
		switch challenge.Scheme {
		case "basic":
			req.SetBasicAuth(c.username, c.password)
			return nil
		case "bearer":
			cacheKey := ""
			scopes := []authScope{c.scope}
			if extraScope != nil {
				// Using ':' as a separator here is unambiguous because getBearerToken below uses the same separator when formatting a remote request (and because repository names can't contain colons).
				cacheKey = fmt.Sprintf("%s:%s", extraScope.remoteName, extraScope.actions)
				scopes = append(scopes, *extraScope)
			}
			var token bearerToken
			t, inCache := c.tokenCache.Load(cacheKey)
			if inCache {
				token = t.(bearerToken)
			}
			if !inCache || time.Now().After(token.expirationTime) {
				t, err := c.getBearerToken(req.Context(), challenge, scopes)
				if err != nil {
					return err
				}
				token = *t
				c.tokenCache.Store(cacheKey, token)
			}
			req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.Token))
			return nil
		default:
			logrus.Debugf("no handler for %s authentication", challenge.Scheme)
		}
	}
	logrus.Infof("None of the challenges sent by server (%s) are supported, trying an unauthenticated request anyway", strings.Join(schemeNames, ", "))
	return nil
}
// we're using the challenges from the /v2/ ping response and not the one from the destination
// URL in this request because:
//
// 1) docker does that as well
// 2) gcr.io is sending 401 without a WWW-Authenticate header in the real request
//
// debugging: https://github.com/containers/image/pull/211#issuecomment-273426236 and follows up