Skip to content

Commit

Permalink
feat: Add validation token endpoint (#487)
Browse files Browse the repository at this point in the history
* feat: Add SERVER_VALIDATION_TOKEN_SECRET option

* feat: Add SERVER_VALIDATION_TOKEN_EXPIRATION option

* feat: Add validation token endpoint

* feat: Add validation token client
  • Loading branch information
kelindi committed Feb 11, 2025
1 parent 2f7bf0f commit 2ccc94d
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 9 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/fatih/color v1.16.0
github.com/go-chi/httprate v0.14.1
github.com/go-git/go-git/v5 v5.10.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.3
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
Expand Down
18 changes: 14 additions & 4 deletions pkg/http/types.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
package http

type ServerOptions struct {
URL string
Host string
Port int
RateLimiter RateLimiterOptions
URL string
Host string
Port int
AccessControl AccessControlOptions
RateLimiter RateLimiterOptions
}

type AccessControlOptions struct {
ValidationTokenSecret string
ValidationTokenExpiration int
}

type ValidationToken struct {
JWT string
}

type RateLimiterOptions struct {
Expand Down
29 changes: 25 additions & 4 deletions pkg/options/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ import (

func GetDefaultServerOptions() http.ServerOptions {
return http.ServerOptions{
URL: GetDefaultServeOptionString("SERVER_URL", ""),
Host: GetDefaultServeOptionString("SERVER_HOST", "0.0.0.0"),
Port: GetDefaultServeOptionInt("SERVER_PORT", 8080), //nolint:gomnd
RateLimiter: GetDefaultRateLimiterOptions(),
URL: GetDefaultServeOptionString("SERVER_URL", ""),
Host: GetDefaultServeOptionString("SERVER_HOST", "0.0.0.0"),
Port: GetDefaultServeOptionInt("SERVER_PORT", 8080), //nolint:gomnd
AccessControl: GetDefaultAccessControlOptions(),
RateLimiter: GetDefaultRateLimiterOptions(),
}
}

func GetDefaultAccessControlOptions() http.AccessControlOptions {
return http.AccessControlOptions{
ValidationTokenSecret: GetDefaultServeOptionString("SERVER_VALIDATION_TOKEN_SECRET", ""),
ValidationTokenExpiration: GetDefaultServeOptionInt("SERVER_VALIDATION_TOKEN_EXPIRATION", 604800), // one week
}
}

Expand All @@ -36,6 +44,16 @@ func AddServerCliFlags(cmd *cobra.Command, serverOptions *http.ServerOptions) {
&serverOptions.Port, "server-port", serverOptions.Port,
`The port to bind the api server to (SERVER_PORT).`,
)
cmd.PersistentFlags().StringVar(
&serverOptions.AccessControl.ValidationTokenSecret, "server-validation-token-secret",
serverOptions.AccessControl.ValidationTokenSecret,
`Secret for generating validation service JWTs (SERVER_VALIDATION_TOKEN_SECRET).`,
)
cmd.PersistentFlags().IntVar(
&serverOptions.AccessControl.ValidationTokenExpiration, "server-validation-token-expiration",
serverOptions.AccessControl.ValidationTokenExpiration,
`Validation service JWT expiration in seconds (SERVER_VALIDATION_TOKEN_EXPIRATION).`,
)
cmd.PersistentFlags().IntVar(
&serverOptions.RateLimiter.RequestLimit, "server-rate-request-limit", serverOptions.RateLimiter.RequestLimit,
`The max requests over the rate window length (SERVER_RATE_REQUEST_LIMIT).`,
Expand All @@ -50,5 +68,8 @@ func CheckServerOptions(options http.ServerOptions) error {
if options.URL == "" {
return fmt.Errorf("SERVER_URL is required")
}
if options.AccessControl.ValidationTokenSecret == "" {
return fmt.Errorf("SERVER_VALIDATION_TOKEN_SECRET is required")
}
return nil
}
6 changes: 6 additions & 0 deletions pkg/solver/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,9 @@ func (client *SolverClient) DownloadResultFiles(id string, localPath string) err
}
return system.ExpandTarBuffer(buf, localPath)
}

// Validation service

func (client *SolverClient) GetValidationToken() (http.ValidationToken, error) {
return http.GetRequest[http.ValidationToken](client.options, fmt.Sprintf("/validation_token"), map[string]string{})
}
40 changes: 40 additions & 0 deletions pkg/solver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"time"

"github.com/go-chi/httprate"
"github.com/golang-jwt/jwt"
"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/lilypad-tech/lilypad/pkg/data"
"github.com/lilypad-tech/lilypad/pkg/http"
Expand Down Expand Up @@ -92,6 +94,8 @@ func (solverServer *solverServer) ListenAndServe(ctx context.Context, cm *system
subrouter.HandleFunc("/deals/{id}/txs/job_creator", http.PostHandler(solverServer.updateTransactionsJobCreator)).Methods("POST")
subrouter.HandleFunc("/deals/{id}/txs/mediator", http.PostHandler(solverServer.updateTransactionsMediator)).Methods("POST")

subrouter.HandleFunc("/validation_token", http.GetHandler(solverServer.getValidationToken)).Methods("GET")

// this will fan out to all connected web socket connections
// we read all events coming from inside the solver controller
// and write them to anyone who is connected to us
Expand Down Expand Up @@ -620,3 +624,39 @@ func (solverServer *solverServer) uploadFiles(res corehttp.ResponseWriter, req *
return
}
}

// Validation Service

func (solverServer *solverServer) getValidationToken(res corehttp.ResponseWriter, req *corehttp.Request) (*http.ValidationToken, error) {
// Check signature
signerAddress, err := http.CheckSignature(req)
if err != nil {
log.Warn().Err(err).Msgf("error checking signature")
return nil, err
}

// Create token with signer address sub
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": "rp_" + signerAddress,
"iss": "kafka-auth",
"aud": "kafka-broker",
"scope": "kafka-cluster",
"iat": time.Now().Unix(),
"exp": time.Now().Add(time.Duration(solverServer.options.AccessControl.ValidationTokenExpiration) * time.Second).Unix(),
"jti": uuid.New().String(),
})

// Add the key ID to the token header
token.Header["kid"] = "key-1"

// Sign the token
secret := []byte(solverServer.options.AccessControl.ValidationTokenSecret)
tokenString, err := token.SignedString(secret)
if err != nil {
log.Error().Err(err).Msgf("failed to sign token")
return nil, errors.New("failed to sign token")
}

// Respond with the JWT
return &http.ValidationToken{JWT: tokenString}, nil
}
3 changes: 2 additions & 1 deletion stack
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,14 @@ function run-cowsay-onchain() {
############################################################################
# solver
#
# Note: The presence of the WEB3_PRIVATE_KEY here is only necessary for local development. You are advised not to import this key into a wallet nor use it for anything other for testing Lilypad locally
# Note: The presence of the WEB3_PRIVATE_KEY and SERVER_VALIDATION_TOKEN_SECRET are only necessary for local development. You are advised not to import this key into a wallet nor use it for anything other for testing Lilypad locally
############################################################################

function solver() {
load-local-env
export WEB3_PRIVATE_KEY=${SOLVER_PRIVATE_KEY}
export LOG_LEVEL=debug
export SERVER_VALIDATION_TOKEN_SECRET=912dd001a6613632c066ca10a19254430db2986a84612882a18f838a6360880e
go run . solver --network dev "$@"
}

Expand Down

0 comments on commit 2ccc94d

Please # to comment.