2020-08-13 20:27:42 +00:00
package rpc
import (
"context"
2020-10-20 05:37:12 +00:00
"fmt"
2023-12-01 20:40:09 +00:00
"net/http"
2020-09-03 23:25:56 +00:00
"strings"
2020-08-13 20:27:42 +00:00
2022-02-25 19:08:43 +00:00
"github.com/golang-jwt/jwt/v4"
2023-12-01 20:40:09 +00:00
"github.com/prysmaticlabs/prysm/v4/api"
2022-08-05 10:52:02 +00:00
"github.com/sirupsen/logrus"
2020-08-13 20:27:42 +00:00
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
2021-10-20 16:37:05 +00:00
// JWTInterceptor is a gRPC unary interceptor to authorize incoming requests.
2020-08-13 20:27:42 +00:00
func ( s * Server ) JWTInterceptor ( ) grpc . UnaryServerInterceptor {
return func (
ctx context . Context ,
req interface { } ,
info * grpc . UnaryServerInfo ,
handler grpc . UnaryHandler ,
) ( interface { } , error ) {
2021-10-20 16:37:05 +00:00
if err := s . authorize ( ctx ) ; err != nil {
return nil , err
2020-08-13 20:27:42 +00:00
}
h , err := handler ( ctx , req )
2022-08-05 10:52:02 +00:00
log . WithError ( err ) . WithFields ( logrus . Fields {
"FullMethod" : info . FullMethod ,
"Server" : info . Server ,
} ) . Debug ( "Request handled" )
2020-08-13 20:27:42 +00:00
return h , err
}
}
2023-12-01 20:40:09 +00:00
// JwtHttpInterceptor is an HTTP handler to authorize a route.
func ( s * Server ) JwtHttpInterceptor ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
// if it's not initialize or has a web prefix
2024-02-01 15:13:52 +00:00
if strings . Contains ( r . URL . Path , api . WebApiUrlPrefix ) || strings . Contains ( r . URL . Path , api . KeymanagerApiPrefix ) {
// ignore some routes
2023-12-01 20:40:09 +00:00
reqToken := r . Header . Get ( "Authorization" )
if reqToken == "" {
http . Error ( w , "unauthorized: no Authorization header passed. Please use an Authorization header with the jwt created in the prysm wallet" , http . StatusUnauthorized )
return
}
2024-02-01 16:59:40 +00:00
tokenParts := strings . Split ( reqToken , "Bearer " )
if len ( tokenParts ) != 2 {
http . Error ( w , "Invalid token format" , http . StatusBadRequest )
return
}
token := tokenParts [ 1 ]
2023-12-01 20:40:09 +00:00
_ , err := jwt . Parse ( token , s . validateJWT )
if err != nil {
2024-01-22 22:16:10 +00:00
http . Error ( w , fmt . Errorf ( "forbidden: could not parse JWT token: %v" , err ) . Error ( ) , http . StatusForbidden )
2023-12-01 20:40:09 +00:00
return
}
}
next . ServeHTTP ( w , r )
} )
}
2020-08-13 20:27:42 +00:00
// Authorize the token received is valid.
func ( s * Server ) authorize ( ctx context . Context ) error {
md , ok := metadata . FromIncomingContext ( ctx )
if ! ok {
2020-09-03 23:25:56 +00:00
return status . Errorf ( codes . InvalidArgument , "Retrieving metadata failed" )
2020-08-13 20:27:42 +00:00
}
authHeader , ok := md [ "authorization" ]
if ! ok {
2020-09-03 23:25:56 +00:00
return status . Errorf ( codes . Unauthenticated , "Authorization token could not be found" )
2020-08-13 20:27:42 +00:00
}
2020-09-03 23:25:56 +00:00
if len ( authHeader ) < 1 || ! strings . Contains ( authHeader [ 0 ] , "Bearer " ) {
return status . Error ( codes . Unauthenticated , "Invalid auth header, needs Bearer {token}" )
}
token := strings . Split ( authHeader [ 0 ] , "Bearer " ) [ 1 ]
2020-10-20 05:37:12 +00:00
_ , err := jwt . Parse ( token , s . validateJWT )
2020-08-13 20:27:42 +00:00
if err != nil {
2020-10-10 02:07:28 +00:00
return status . Errorf ( codes . Unauthenticated , "Could not parse JWT token: %v" , err )
2020-08-13 20:27:42 +00:00
}
return nil
}
2020-10-20 05:37:12 +00:00
func ( s * Server ) validateJWT ( token * jwt . Token ) ( interface { } , error ) {
if _ , ok := token . Method . ( * jwt . SigningMethodHMAC ) ; ! ok {
return nil , fmt . Errorf ( "unexpected JWT signing method: %v" , token . Header [ "alg" ] )
}
2021-10-28 14:24:39 +00:00
return s . jwtSecret , nil
2020-10-20 05:37:12 +00:00
}