Codebase list golang-github-go-kit-kit / 6b4ca49 auth / basic / middleware.go
6b4ca49

Tree @6b4ca49 (Download .tar.gz)

middleware.go @6b4ca49

b48aaea
 
 
8c6ad4e
b48aaea
 
 
 
 
 
 
 
 
 
 
 
37a2080
b48aaea
 
 
 
37a2080
b48aaea
 
 
 
8c6ad4e
b48aaea
 
 
 
37a2080
b48aaea
 
 
 
 
 
 
 
8c6ad4e
 
b48aaea
 
 
 
 
 
 
 
8c6ad4e
 
b48aaea
 
 
8c6ad4e
b48aaea
 
37a2080
 
 
 
 
 
8c6ad4e
b48aaea
37a2080
 
8c6ad4e
b48aaea
 
 
37a2080
b48aaea
 
 
 
37a2080
 
8c6ad4e
37a2080
 
b48aaea
 
 
 
 
 
 
package basic

import (
	"bytes"
	"context"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/base64"
	"fmt"
	"net/http"
	"strings"

	"github.com/go-kit/kit/endpoint"
	httptransport "github.com/go-kit/kit/transport/http"
)

// AuthError represents an authorization error.
type AuthError struct {
	Realm string
}

// StatusCode is an implementation of the StatusCoder interface in go-kit/http.
func (AuthError) StatusCode() int {
	return http.StatusUnauthorized
}

// Error is an implementation of the Error interface.
func (AuthError) Error() string {
	return http.StatusText(http.StatusUnauthorized)
}

// Headers is an implementation of the Headerer interface in go-kit/http.
func (e AuthError) Headers() http.Header {
	return http.Header{
		"Content-Type":           []string{"text/plain; charset=utf-8"},
		"X-Content-Type-Options": []string{"nosniff"},
		"WWW-Authenticate":       []string{fmt.Sprintf(`Basic realm=%q`, e.Realm)}}
}

// parseBasicAuth parses an HTTP Basic Authentication string.
// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ([]byte("Aladdin"), []byte("open sesame"), true).
func parseBasicAuth(auth string) (username, password []byte, ok bool) {
	const prefix = "Basic "
	if !strings.HasPrefix(auth, prefix) {
		return
	}
	c, err := base64.StdEncoding.DecodeString(auth[len(prefix):])
	if err != nil {
		return
	}

	s := bytes.IndexByte(c, ':')
	if s < 0 {
		return
	}
	return c[:s], c[s+1:], true
}

// Returns a hash of a given slice.
func toHashSlice(s []byte) []byte {
	hash := sha256.Sum256(s)
	return hash[:]
}

// AuthMiddleware returns a Basic Authentication middleware for a particular user and password.
func AuthMiddleware(requiredUser, requiredPassword, realm string) endpoint.Middleware {
	requiredUserBytes := toHashSlice([]byte(requiredUser))
	requiredPasswordBytes := toHashSlice([]byte(requiredPassword))

	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (interface{}, error) {
			auth := ctx.Value(httptransport.ContextKeyRequestAuthorization).(string)
			givenUser, givenPassword, ok := parseBasicAuth(auth)
			if !ok {
				return nil, AuthError{realm}
			}

			givenUserBytes := toHashSlice(givenUser)
			givenPasswordBytes := toHashSlice(givenPassword)

			if subtle.ConstantTimeCompare(givenUserBytes, requiredUserBytes) == 0 ||
				subtle.ConstantTimeCompare(givenPasswordBytes, requiredPasswordBytes) == 0 {
				return nil, AuthError{realm}
			}

			return next(ctx, request)
		}
	}
}