func (b *BasicHandler) ProcessChallenge(c Challenge) error {
	if _, ok := c.params["realm"]; !ok {
		return ErrInvalidChallenge
	}
	if b.realm != c.params["realm"] {
		b.realm = c.params["realm"]
		return nil
	}
	return ErrNoNewChallenge
}
ProcessChallenge for BasicHandler is a noop
package auth
import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
	"github.com/sirupsen/logrus"
)
type charLU byte
var charLUs [256]charLU
var defaultClientID = "regclient"
var minTokenLife = 60
var tokenBuffer = time.Second * 5
const (
	isSpace charLU = 1 << iota
	isAlphaNum
)
func init() {
	for c := 0; c < 256; c++ {
		charLUs[c] = 0
		if strings.ContainsRune(" \t\r\n", rune(c)) {
			charLUs[c] |= isSpace
		}
		if (rune('a') <= rune(c) && rune(c) <= rune('z')) || (rune('A') <= rune(c) && rune(c) <= rune('Z') || (rune('0') <= rune(c) && rune(c) <= rune('9'))) {
			charLUs[c] |= isAlphaNum
		}
	}
}
type CredsFn func(string) Cred
type Cred struct {
	User, Password, Token string
}
type Auth interface {
	AddScope(host, scope string) error
	HandleResponse(*http.Response) error
	UpdateRequest(*http.Request) error
}
type Challenge struct {
	authType string
	params   map[string]string
}
type Handler interface {
	AddScope(scope string) error
	ProcessChallenge(Challenge) error
	GenerateAuth() (string, error)
}
type HandlerBuild func(client *http.Client, clientID, host string, cred Cred) Handler
type Opts func(*auth)
type auth struct {
	httpClient *http.Client
	clientID   string
	credsFn    CredsFn
	hbs        map[string]HandlerBuild        
	hs         map[string]map[string]Handler  
	authTypes  []string
	log        *logrus.Logger
	mu         sync.Mutex
}
func NewAuth(opts ...Opts) Auth {
	a := &auth{
		httpClient: &http.Client{},
		clientID:   defaultClientID,
		credsFn:    DefaultCredsFn,
		hbs:        map[string]HandlerBuild{},
		hs:         map[string]map[string]Handler{},
		authTypes:  []string{},
	}
	a.log = &logrus.Logger{
		Out:       os.Stderr,
		Formatter: new(logrus.TextFormatter),
		Hooks:     make(logrus.LevelHooks),
		Level:     logrus.WarnLevel,
	}
	for _, opt := range opts {
		opt(a)
	}
	if len(a.authTypes) == 0 {
		a.addDefaultHandlers()
	}
	return a
}
func WithCreds(f CredsFn) Opts {
	return func(a *auth) {
		if f != nil {
			a.credsFn = f
		}
	}
}
func WithHTTPClient(h *http.Client) Opts {
	return func(a *auth) {
		if h != nil {
			a.httpClient = h
		}
	}
}
func WithClientID(clientID string) Opts {
	return func(a *auth) {
		a.clientID = clientID
	}
}
func WithHandler(authType string, hb HandlerBuild) Opts {
	return func(a *auth) {
		lcat := strings.ToLower(authType)
		a.hbs[lcat] = hb
		a.authTypes = append(a.authTypes, lcat)
	}
}
func WithDefaultHandlers() Opts {
	return func(a *auth) {
		a.addDefaultHandlers()
	}
}
func WithLog(log *logrus.Logger) Opts {
	return func(a *auth) {
		a.log = log
	}
}
func (a *auth) AddScope(host, scope string) error {
	success := false
	for _, at := range a.authTypes {
		if a.hs[host][at] != nil {
			err := a.hs[host][at].AddScope(scope)
			if err == nil {
				success = true
			} else if err != ErrNoNewChallenge {
				return err
			}
		}
	}
	if !success {
		return ErrNoNewChallenge
	}
	a.log.WithFields(logrus.Fields{
		"host":  host,
		"scope": scope,
	}).Debug("Auth scope added")
	return nil
}
func (a *auth) HandleResponse(resp *http.Response) error {
	a.mu.Lock()
	defer a.mu.Unlock()
	if resp.StatusCode != http.StatusUnauthorized {
		return ErrUnsupported
	}
	host := resp.Request.URL.Host
	cl, err := ParseAuthHeaders(resp.Header.Values("WWW-Authenticate"))
	if err != nil {
		return err
	}
	a.log.WithFields(logrus.Fields{
		"challenge": cl,
	}).Debug("Auth request parsed")
	if len(cl) < 1 {
		return ErrEmptyChallenge
	}
	goodChallenge := false
	for _, c := range cl {
		if _, ok := a.hbs[c.authType]; !ok {
			a.log.WithFields(logrus.Fields{
				"authtype": c.authType,
			}).Warn("Unsupported auth type")
			continue
		}
		if _, ok := a.hs[host]; !ok {
			a.hs[host] = map[string]Handler{}
		}
		if _, ok := a.hs[host][c.authType]; !ok {
			h := a.hbs[c.authType](a.httpClient, a.clientID, host, a.credsFn(host))
			if h == nil {
				continue
			}
			a.hs[host][c.authType] = h
		}
		err := a.hs[host][c.authType].ProcessChallenge(c)
		if err == nil {
			goodChallenge = true
		} else if err == ErrNoNewChallenge {
			prevAH := resp.Request.Header.Get("Authorization")
			ah, err := a.hs[host][c.authType].GenerateAuth()
			if err == nil && prevAH != ah {
				goodChallenge = true
			}
		} else {
			return err
		}
	}
	if goodChallenge == false {
		return ErrUnauthorized
	}
	return nil
}
func (a *auth) UpdateRequest(req *http.Request) error {
	a.mu.Lock()
	defer a.mu.Unlock()
	host := req.URL.Host
	if a.hs[host] == nil {
		return nil
	}
	var err error
	var ah string
	for _, at := range a.authTypes {
		if a.hs[host][at] != nil {
			ah, err = a.hs[host][at].GenerateAuth()
			if err != nil {
				a.log.WithFields(logrus.Fields{
					"err":      err,
					"host":     host,
					"authtype": at,
				}).Debug("Failed to generate auth")
				continue
			}
			req.Header.Set("Authorization", ah)
			break
		}
	}
	return err
}
func (a *auth) addDefaultHandlers() {
	if _, ok := a.hbs["basic"]; !ok {
		a.hbs["basic"] = NewBasicHandler
		a.authTypes = append(a.authTypes, "basic")
	}
	if _, ok := a.hbs["bearer"]; !ok {
		a.hbs["bearer"] = NewBearerHandler
		a.authTypes = append(a.authTypes, "bearer")
	}
	if _, ok := a.hbs["jwt"]; !ok {
		a.hbs["jwt"] = NewJWTHandler
		a.authTypes = append(a.authTypes, "jwt")
	}
}
func DefaultCredsFn(h string) Cred {
	return Cred{}
}
func ParseAuthHeaders(ahl []string) ([]Challenge, error) {
	var cl []Challenge
	for _, ah := range ahl {
		c, err := ParseAuthHeader(ah)
		if err != nil {
			return nil, fmt.Errorf("Failed to parse challenge header: %s", ah)
		}
		cl = append(cl, c...)
	}
	return cl, nil
}
func ParseAuthHeader(ah string) ([]Challenge, error) {
	var cl []Challenge
	var c *Challenge
	var eb, atb, kb, vb []byte  
	state := "string"
	for _, b := range []byte(ah) {
		switch state {
		case "string":
			if len(eb) == 0 {
				if b == '"' {  
					state = "quoted"
				} else if charLUs[b]&isAlphaNum != 0 {
					eb = append(eb, b)
				} else if charLUs[b]&isSpace != 0 {
				} else {
					return nil, ErrParseFailure
				}
			} else {
				if charLUs[b]&isAlphaNum != 0 {
					eb = append(eb, b)
				} else if b == '=' && len(atb) > 0 {
					kb = eb
					eb = []byte{}
					state = "value"
				} else if charLUs[b]&isSpace != 0 {
					atb = eb
					eb = []byte{}
					c = &Challenge{authType: strings.ToLower(string(atb)), params: map[string]string{}}
					cl = append(cl, *c)
				} else {
					return nil, ErrParseFailure
				}
			}
		case "value":
			if charLUs[b]&isAlphaNum != 0 {
				vb = append(vb, b)
			} else if b == '"' && len(vb) == 0 {
				state = "quoted"
			} else if charLUs[b]&isSpace != 0 || b == ',' {
				c.params[strings.ToLower(string(kb))] = string(vb)
				kb = []byte{}
				vb = []byte{}
				if b == ',' {
					state = "string"
				} else {
					state = "endvalue"
				}
			} else {
				return nil, ErrParseFailure
			}
		case "quoted":
			if b == '"' {
				c.params[strings.ToLower(string(kb))] = string(vb)
				kb = []byte{}
				vb = []byte{}
				state = "endvalue"
			} else if b == '\\' {
				state = "escape"
			} else {
				vb = append(vb, b)
			}
		case "endvalue":
			if charLUs[b]&isSpace != 0 {
			} else if b == ',' {
				state = "string"
			} else {
				return nil, ErrParseFailure
			}
		case "escape":
			vb = append(vb, b)
			state = "quoted"
		default:
			return nil, ErrParseFailure
		}
	}
	switch state {
	case "string":
		if len(eb) != 0 {
			atb = eb
			c = &Challenge{authType: strings.ToLower(string(atb)), params: map[string]string{}}
			cl = append(cl, *c)
		}
	case "value":
		if len(vb) != 0 {
			c.params[strings.ToLower(string(kb))] = string(vb)
		}
	case "quoted", "escape":
		return nil, ErrParseFailure
	}
	return cl, nil
}
type BasicHandler struct {
	realm string
	cred  Cred
}
func NewBasicHandler(client *http.Client, clientID, host string, cred Cred) Handler {
	return &BasicHandler{
		realm: "",
		cred:  cred,
	}
}
func (b *BasicHandler) AddScope(scope string) error {
	return ErrNoNewChallenge
}