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 }