github.com/prysmaticlabs/prysm@v1.4.4/validator/rpc/intercepter.go (about) 1 package rpc 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 "sync" 8 9 "github.com/form3tech-oss/jwt-go" 10 "google.golang.org/grpc" 11 "google.golang.org/grpc/codes" 12 "google.golang.org/grpc/metadata" 13 "google.golang.org/grpc/status" 14 ) 15 16 // noAuthPaths keeps track of the paths which do not require 17 // authentication from our API. 18 var ( 19 noAuthPaths = map[string]bool{ 20 "/ethereum.validator.accounts.v2.Auth/Signup": true, 21 "/ethereum.validator.accounts.v2.Auth/Login": true, 22 "/ethereum.validator.accounts.v2.Auth/Logout": true, 23 "/ethereum.validator.accounts.v2.Auth/HasUsedWeb": true, 24 "/ethereum.validator.accounts.v2.Wallet/HasWallet": true, 25 "/ethereum.validator.accounts.v2.Beacon/GetBeaconStatus": true, 26 "/ethereum.validator.accounts.v2.Beacon/GetValidatorParticipation": true, 27 "/ethereum.validator.accounts.v2.Beacon/GetValidatorPerformance": true, 28 "/ethereum.validator.accounts.v2.Beacon/GetValidatorBalances": true, 29 "/ethereum.validator.accounts.v2.Beacon/GetValidators": true, 30 "/ethereum.validator.accounts.v2.Beacon/GetValidatorQueue": true, 31 "/ethereum.validator.accounts.v2.Beacon/GetPeers": true, 32 "/ethereum.validator.accounts.v2.Beacon/StreamValidatorLogs": true, 33 } 34 authLock sync.RWMutex 35 ) 36 37 // JWTInterceptor is a gRPC unary interceptor to authorize incoming requests 38 // for methods that are NOT in the noAuthPaths configuration map. 39 func (s *Server) JWTInterceptor() grpc.UnaryServerInterceptor { 40 return func( 41 ctx context.Context, 42 req interface{}, 43 info *grpc.UnaryServerInfo, 44 handler grpc.UnaryHandler, 45 ) (interface{}, error) { 46 // Skip authorize when the path doesn't require auth. 47 authLock.RLock() 48 shouldAuthenticate := !noAuthPaths[info.FullMethod] 49 authLock.RUnlock() 50 if shouldAuthenticate { 51 if err := s.authorize(ctx); err != nil { 52 return nil, err 53 } 54 } 55 56 h, err := handler(ctx, req) 57 log.Debugf("Request - Method: %s, Error: %v\n", info.FullMethod, err) 58 return h, err 59 } 60 } 61 62 // Authorize the token received is valid. 63 func (s *Server) authorize(ctx context.Context) error { 64 md, ok := metadata.FromIncomingContext(ctx) 65 if !ok { 66 return status.Errorf(codes.InvalidArgument, "Retrieving metadata failed") 67 } 68 69 authHeader, ok := md["authorization"] 70 if !ok { 71 return status.Errorf(codes.Unauthenticated, "Authorization token could not be found") 72 } 73 if len(authHeader) < 1 || !strings.Contains(authHeader[0], "Bearer ") { 74 return status.Error(codes.Unauthenticated, "Invalid auth header, needs Bearer {token}") 75 } 76 token := strings.Split(authHeader[0], "Bearer ")[1] 77 _, err := jwt.Parse(token, s.validateJWT) 78 if err != nil { 79 return status.Errorf(codes.Unauthenticated, "Could not parse JWT token: %v", err) 80 } 81 return nil 82 } 83 84 func (s *Server) validateJWT(token *jwt.Token) (interface{}, error) { 85 if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 86 return nil, fmt.Errorf("unexpected JWT signing method: %v", token.Header["alg"]) 87 } 88 return s.jwtKey, nil 89 }