github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/api/auth.go (about)

     1  package api
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"strings"
     7  
     8  	"github.com/pyroscope-io/pyroscope/pkg/model"
     9  	"github.com/pyroscope-io/pyroscope/pkg/server/httputils"
    10  )
    11  
    12  const JWTCookieName = "pyroscopeJWT"
    13  
    14  //go:generate mockgen -destination mocks/auth.go -package mocks . AuthService
    15  
    16  type AuthService interface {
    17  	APIKeyFromToken(ctx context.Context, token string) (model.APIKey, error)
    18  	UserFromJWTToken(ctx context.Context, token string) (model.User, error)
    19  	AuthenticateUser(ctx context.Context, name, password string) (model.User, error)
    20  }
    21  
    22  // AuthMiddleware authenticates requests.
    23  func AuthMiddleware(loginRedirect http.HandlerFunc, authService AuthService, h httputils.Utils) func(next http.Handler) http.Handler {
    24  	return func(next http.Handler) http.Handler {
    25  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    26  			if token, ok := extractTokenFromAuthHeader(r.Header.Get("Authorization")); ok {
    27  				k, err := authService.APIKeyFromToken(r.Context(), token)
    28  				if err != nil {
    29  					h.HandleError(r, w, model.AuthenticationError{Err: err})
    30  					return
    31  				}
    32  				next.ServeHTTP(w, r.WithContext(model.WithAPIKey(r.Context(), k)))
    33  				return
    34  			}
    35  
    36  			if c, err := r.Cookie(JWTCookieName); err == nil {
    37  				var u model.User
    38  				if u, err = authService.UserFromJWTToken(r.Context(), c.Value); err != nil {
    39  					if loginRedirect != nil {
    40  						h.Logger(r).WithError(err).Debug("failed to authenticate jwt cookie")
    41  						loginRedirect(w, r)
    42  						return
    43  					}
    44  					h.HandleError(r, w, model.AuthenticationError{Err: err})
    45  					return
    46  				}
    47  				next.ServeHTTP(w, r.WithContext(model.WithUser(r.Context(), u)))
    48  				return
    49  			}
    50  
    51  			h.Logger(r).Debug("unauthenticated request")
    52  			if loginRedirect != nil {
    53  				loginRedirect(w, r)
    54  				return
    55  			}
    56  
    57  			h.HandleError(r, w, model.ErrCredentialsInvalid)
    58  		})
    59  	}
    60  }
    61  
    62  const bearer string = "bearer"
    63  
    64  func extractTokenFromAuthHeader(val string) (token string, ok bool) {
    65  	authHeaderParts := strings.Split(val, " ")
    66  	if len(authHeaderParts) != 2 || !strings.EqualFold(authHeaderParts[0], bearer) {
    67  		return "", false
    68  	}
    69  	return authHeaderParts[1], true
    70  }