Skip to content

Commit

Permalink
jwt validation v1
Browse files Browse the repository at this point in the history
  • Loading branch information
Marketen committed May 23, 2024
1 parent e6c7191 commit e2e36bc
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 3 deletions.
1 change: 1 addition & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ services:
volumes:
- ./listener/cmd:/app/cmd
- ./listener/internal:/app/internal
- ./jwt:/app/jwt
networks:
dncore_network:
aliases:
Expand Down
3 changes: 2 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ services:
- mongo
container_name: listener
restart: always

volumes:
- ./jwt:/app/jwt
ui:
build:
context: ui
Expand Down
1 change: 1 addition & 0 deletions listener/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/dappnode/validator-monitoring/listener
go 1.22.0

require (
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/gorilla/mux v1.8.1
github.com/herumi/bls-eth-go-binary v1.35.0
github.com/robfig/cron v1.2.0
Expand Down
2 changes: 2 additions & 0 deletions listener/go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
Expand Down
113 changes: 113 additions & 0 deletions listener/internal/api/middleware/loadSecrets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package middleware

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"strings"
"time"

"github.com/dappnode/validator-monitoring/listener/internal/logger"
"github.com/golang-jwt/jwt/v5"
)

var allowedPublicKeys []string

// Runs automatically in main.go when the package is imported
func init() {
// Load public keys from a JSON file
data, err := os.ReadFile("/app/jwt/public_keys.json")
if err != nil {
logger.Fatal("Failed to load public keys: " + err.Error())
}

var keys struct {
AllowedPublicKeys []string `json:"allowedPublicKeys"`
}

err = json.Unmarshal(data, &keys)
if err != nil {
logger.Fatal("Failed to unmarshal public keys: " + err.Error())
}

allowedPublicKeys = keys.AllowedPublicKeys
logger.Info("Loaded public keys: " + fmt.Sprintln(allowedPublicKeys))
}

// CustomClaims defines the custom claims in the JWT token
type MyCustomClaims struct {
PubKey string `json:"pubkey"`
jwt.RegisteredClaims
}

// JWTMiddleware is a middleware that checks the Authorization header for a valid JWT token
// and verifies the signature using the public key of the user contained in the token.
// The public keys are loaded from a JSON file.
// The JWT token must be in the format "Bearer <token string>"
func JWTMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Missing Authorization header", http.StatusUnauthorized)
return
}

tokenString := strings.TrimPrefix(authHeader, "Bearer ")
if tokenString == authHeader {
http.Error(w, "Invalid Authorization header format", http.StatusUnauthorized)
return
}

// Parse the token
token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
// Validate the algorithm
if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
return nil, errors.New("unexpected signing method")
}

// Extract the claims
claims, ok := token.Claims.(*MyCustomClaims)
logger.Info("token:" + fmt.Sprintln(token))
if !ok {
return nil, errors.New("invalid token claims")
}

// Verify the expiration time
if claims.ExpiresAt != nil && !claims.ExpiresAt.Time.After(time.Now()) {
return nil, errors.New("token has expired, it expires at " + claims.ExpiresAt.Time.String() + " now is " + time.Now().String())
}

// Verify that the public key is allowed
isAllowed := false
for _, allowedKey := range allowedPublicKeys {
logger.Info("allowedKey:" + allowedKey)
logger.Info("claims.PubKey:" + claims.PubKey)
if allowedKey == claims.PubKey {
isAllowed = true
break
}
}

if !isAllowed {
return nil, errors.New("public key not allowed")
}

// Parse and return the public key
pubKey, err := jwt.ParseECPublicKeyFromPEM([]byte(claims.PubKey))
if err != nil {
return nil, err
}
logger.Info("pubKey:" + fmt.Sprintln(pubKey))
return pubKey, nil
})

if err != nil || !token.Valid {
http.Error(w, "Invalid token: "+err.Error(), http.StatusUnauthorized)
return
}

next.ServeHTTP(w, r)
})
}
8 changes: 6 additions & 2 deletions listener/internal/api/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net/http"

"github.com/dappnode/validator-monitoring/listener/internal/api/handlers"
"github.com/dappnode/validator-monitoring/listener/internal/api/middleware"
"github.com/dappnode/validator-monitoring/listener/internal/api/types"
"github.com/gorilla/mux"
"go.mongodb.org/mongo-driver/mongo"
Expand All @@ -19,8 +20,11 @@ func SetupRouter(dbCollection *mongo.Collection, beaconNodeUrls map[types.Networ
handlers.PostSignatures(w, r, dbCollection, beaconNodeUrls, maxEntriesPerBson)
}).Methods(http.MethodPost)

// Middlewares
// r.Use(corsmiddleware()))
//TODO: implement handler for GET signatures
r.Handle("/signatures", middleware.JWTMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Assuming you have a handler for GET signatures
handlers.PostSignatures(w, r, dbCollection, beaconNodeUrls)
}))).Methods(http.MethodGet)

return r
}

0 comments on commit e2e36bc

Please sign in to comment.