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  }