From 63835efcadf71d4063eeb2ecd0162cfc2724ca37 Mon Sep 17 00:00:00 2001 From: Oleg Kovalov Date: Thu, 6 Jun 2024 16:37:57 +0200 Subject: [PATCH 1/6] chore: bump cristalhq/jwt to v5 --- api/rpc/perms/permissions.go | 10 +++++++--- api/rpc/server.go | 12 +++++++----- api/rpc_test.go | 6 +++--- cmd/auth.go | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- libs/authtoken/authtoken.go | 12 ++++++------ nodebuilder/node.go | 2 +- nodebuilder/node/admin.go | 16 +++++++++------- nodebuilder/node/auth.go | 6 +++--- nodebuilder/node/module.go | 6 +++--- nodebuilder/rpc/constructors.go | 6 +++--- 12 files changed, 47 insertions(+), 39 deletions(-) diff --git a/api/rpc/perms/permissions.go b/api/rpc/perms/permissions.go index 00cb056ca9..1bedb1eefc 100644 --- a/api/rpc/perms/permissions.go +++ b/api/rpc/perms/permissions.go @@ -3,7 +3,7 @@ package perms import ( "encoding/json" - "github.com/cristalhq/jwt" + "github.com/cristalhq/jwt/v5" "github.com/filecoin-project/go-jsonrpc/auth" ) @@ -28,9 +28,13 @@ func (j *JWTPayload) MarshalBinary() (data []byte, err error) { // NewTokenWithPerms generates and signs a new JWT token with the given secret // and given permissions. -func NewTokenWithPerms(secret jwt.Signer, perms []auth.Permission) ([]byte, error) { +func NewTokenWithPerms(signer jwt.Signer, perms []auth.Permission) ([]byte, error) { p := &JWTPayload{ Allow: perms, } - return jwt.NewTokenBuilder(secret).BuildBytes(p) + token, err := jwt.NewBuilder(signer).Build(p) + if err != nil { + return nil, err + } + return token.Bytes(), nil } diff --git a/api/rpc/server.go b/api/rpc/server.go index 0bcc9325c0..1693bd0de7 100644 --- a/api/rpc/server.go +++ b/api/rpc/server.go @@ -8,7 +8,7 @@ import ( "sync/atomic" "time" - "github.com/cristalhq/jwt" + "github.com/cristalhq/jwt/v5" "github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-jsonrpc/auth" logging "github.com/ipfs/go-log/v2" @@ -27,10 +27,11 @@ type Server struct { started atomic.Bool - auth jwt.Signer + signer jwt.Signer + verifier jwt.Verifier } -func NewServer(address, port string, authDisabled bool, secret jwt.Signer) *Server { +func NewServer(address, port string, authDisabled bool, signer jwt.Signer, verifier jwt.Verifier) *Server { rpc := jsonrpc.NewServer() srv := &Server{ rpc: rpc, @@ -39,7 +40,8 @@ func NewServer(address, port string, authDisabled bool, secret jwt.Signer) *Serv // the amount of time allowed to read request headers. set to the default 2 seconds ReadHeaderTimeout: 2 * time.Second, }, - auth: secret, + signer: signer, + verifier: verifier, authDisabled: authDisabled, } srv.srv.Handler = &auth.Handler{ @@ -56,7 +58,7 @@ func (s *Server) verifyAuth(_ context.Context, token string) ([]auth.Permission, if s.authDisabled { return perms.AllPerms, nil } - return authtoken.ExtractSignedPermissions(s.auth, token) + return authtoken.ExtractSignedPermissions(s.verifier, token) } // RegisterService registers a service onto the RPC server. All methods on the service will then be diff --git a/api/rpc_test.go b/api/rpc_test.go index e019412f4d..454dace22a 100644 --- a/api/rpc_test.go +++ b/api/rpc_test.go @@ -9,7 +9,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cristalhq/jwt" + "github.com/cristalhq/jwt/v5" "github.com/golang/mock/gomock" "github.com/libp2p/go-libp2p/core/network" "github.com/stretchr/testify/require" @@ -46,7 +46,7 @@ func TestRPCCallsUnderlyingNode(t *testing.T) { t.Cleanup(cancel) // generate dummy signer and sign admin perms token with it - signer, err := jwt.NewHS256(make([]byte, 32)) + signer, err := jwt.NewSignerHS(jwt.HS256, make([]byte, 32)) require.NoError(t, err) nd, server := setupNodeWithAuthedRPC(t, signer) @@ -123,7 +123,7 @@ func TestAuthedRPC(t *testing.T) { t.Cleanup(cancel) // generate dummy signer and sign admin perms token with it - signer, err := jwt.NewHS256(make([]byte, 32)) + signer, err := jwt.NewSignerHS(jwt.HS256, make([]byte, 32)) require.NoError(t, err) nd, server := setupNodeWithAuthedRPC(t, signer) diff --git a/cmd/auth.go b/cmd/auth.go index d79a320d54..b29497d487 100644 --- a/cmd/auth.go +++ b/cmd/auth.go @@ -7,7 +7,7 @@ import ( "io" "path/filepath" - "github.com/cristalhq/jwt" + "github.com/cristalhq/jwt/v5" "github.com/filecoin-project/go-jsonrpc/auth" "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" @@ -74,7 +74,7 @@ func newKeystore(path string) (keystore.Keystore, error) { } func buildJWTToken(body []byte, permissions []auth.Permission) (string, error) { - signer, err := jwt.NewHS256(body) + signer, err := jwt.NewSignerHS(jwt.HS256, body) if err != nil { return "", err } diff --git a/go.mod b/go.mod index ae933d8f8f..aef441ce56 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/celestiaorg/nmt v0.21.0 github.com/celestiaorg/rsmt2d v0.13.1 github.com/cosmos/cosmos-sdk v0.46.16 - github.com/cristalhq/jwt v1.2.0 + github.com/cristalhq/jwt/v5 v5.4.0 github.com/dgraph-io/badger/v4 v4.2.1-0.20240106094458-1c417aa3799c github.com/etclabscore/go-openrpc-reflect v0.0.37 github.com/filecoin-project/dagstore v0.5.6 diff --git a/go.sum b/go.sum index 2a4d85e41b..4f6864ca24 100644 --- a/go.sum +++ b/go.sum @@ -503,8 +503,8 @@ github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6V github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cristalhq/jwt v1.2.0 h1:fHmMkFJvEbS4o04aQP8BmtJg7fqkYvd7r8er3sUdS4Q= -github.com/cristalhq/jwt v1.2.0/go.mod h1:QQFazsDzoqeucUEEV0h16uPTZXBAi2SVA8cQ9JEDuFw= +github.com/cristalhq/jwt/v5 v5.4.0 h1:Wxi1TocFHaijyV608j7v7B9mPc4ZNjvWT3LKBO0d4QI= +github.com/cristalhq/jwt/v5 v5.4.0/go.mod h1:+b/BzaCWEpFDmXxspJ5h4SdJ1N/45KMjKOetWzmHvDA= github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= diff --git a/libs/authtoken/authtoken.go b/libs/authtoken/authtoken.go index 3d6645c972..873f58b6a3 100644 --- a/libs/authtoken/authtoken.go +++ b/libs/authtoken/authtoken.go @@ -3,7 +3,7 @@ package authtoken import ( "encoding/json" - "github.com/cristalhq/jwt" + "github.com/cristalhq/jwt/v5" "github.com/filecoin-project/go-jsonrpc/auth" "github.com/celestiaorg/celestia-node/api/rpc/perms" @@ -11,13 +11,13 @@ import ( // ExtractSignedPermissions returns the permissions granted to the token by the passed signer. // If the token isn't signed by the signer, it will not pass verification. -func ExtractSignedPermissions(signer jwt.Signer, token string) ([]auth.Permission, error) { - tk, err := jwt.ParseAndVerifyString(token, signer) +func ExtractSignedPermissions(verifier jwt.Verifier, token string) ([]auth.Permission, error) { + tk, err := jwt.Parse([]byte(token), verifier) if err != nil { return nil, err } p := new(perms.JWTPayload) - err = json.Unmarshal(tk.RawClaims(), p) + err = json.Unmarshal(tk.Claims(), p) if err != nil { return nil, err } @@ -26,11 +26,11 @@ func ExtractSignedPermissions(signer jwt.Signer, token string) ([]auth.Permissio // NewSignedJWT returns a signed JWT token with the passed permissions and signer. func NewSignedJWT(signer jwt.Signer, permissions []auth.Permission) (string, error) { - token, err := jwt.NewTokenBuilder(signer).Build(&perms.JWTPayload{ + token, err := jwt.NewBuilder(signer).Build(&perms.JWTPayload{ Allow: permissions, }) if err != nil { return "", err } - return token.InsecureString(), nil + return token.String(), nil } diff --git a/nodebuilder/node.go b/nodebuilder/node.go index 9ec1c4d4e0..e17c9d3922 100644 --- a/nodebuilder/node.go +++ b/nodebuilder/node.go @@ -6,7 +6,7 @@ import ( "fmt" "strings" - "github.com/cristalhq/jwt" + "github.com/cristalhq/jwt/v5" "github.com/ipfs/boxo/blockservice" "github.com/ipfs/boxo/exchange" logging "github.com/ipfs/go-log/v2" diff --git a/nodebuilder/node/admin.go b/nodebuilder/node/admin.go index 387bb0408b..fb21962c96 100644 --- a/nodebuilder/node/admin.go +++ b/nodebuilder/node/admin.go @@ -3,7 +3,7 @@ package node import ( "context" - "github.com/cristalhq/jwt" + "github.com/cristalhq/jwt/v5" "github.com/filecoin-project/go-jsonrpc/auth" logging "github.com/ipfs/go-log/v2" @@ -13,14 +13,16 @@ import ( var APIVersion = GetBuildInfo().SemanticVersion type module struct { - tp Type - signer jwt.Signer + tp Type + signer jwt.Signer + verifier jwt.Verifier } -func newModule(tp Type, signer jwt.Signer) Module { +func newModule(tp Type, signer jwt.Signer, verifier jwt.Verifier) Module { return &module{ - tp: tp, - signer: signer, + tp: tp, + signer: signer, + verifier: verifier, } } @@ -51,7 +53,7 @@ func (m *module) LogLevelSet(_ context.Context, name, level string) error { } func (m *module) AuthVerify(_ context.Context, token string) ([]auth.Permission, error) { - return authtoken.ExtractSignedPermissions(m.signer, token) + return authtoken.ExtractSignedPermissions(m.verifier, token) } func (m *module) AuthNew(_ context.Context, permissions []auth.Permission) (string, error) { diff --git a/nodebuilder/node/auth.go b/nodebuilder/node/auth.go index 9c16af92c3..9ae6d29d67 100644 --- a/nodebuilder/node/auth.go +++ b/nodebuilder/node/auth.go @@ -4,7 +4,7 @@ import ( "crypto/rand" "io" - "github.com/cristalhq/jwt" + "github.com/cristalhq/jwt/v5" "github.com/celestiaorg/celestia-node/libs/keystore" ) @@ -16,7 +16,7 @@ var SecretName = keystore.KeyName("jwt-secret.jwt") func secret(ks keystore.Keystore) (jwt.Signer, error) { // if key already exists, use it if pk, ok := existing(ks); ok { - return jwt.NewHS256(pk) + return jwt.NewSignerHS(jwt.HS256, pk) } // otherwise, generate and save new priv key sk, err := io.ReadAll(io.LimitReader(rand.Reader, 32)) @@ -29,7 +29,7 @@ func secret(ks keystore.Keystore) (jwt.Signer, error) { return nil, err } - return jwt.NewHS256(sk) + return jwt.NewSignerHS(jwt.HS256, sk) } func existing(ks keystore.Keystore) ([]byte, bool) { diff --git a/nodebuilder/node/module.go b/nodebuilder/node/module.go index 5abfad8e5f..47ce4363ac 100644 --- a/nodebuilder/node/module.go +++ b/nodebuilder/node/module.go @@ -1,15 +1,15 @@ package node import ( - "github.com/cristalhq/jwt" + "github.com/cristalhq/jwt/v5" "go.uber.org/fx" ) func ConstructModule(tp Type) fx.Option { return fx.Module( "node", - fx.Provide(func(secret jwt.Signer) Module { - return newModule(tp, secret) + fx.Provide(func(signer jwt.Signer, verifier jwt.Verifier) Module { + return newModule(tp, signer, verifier) }), fx.Provide(secret), ) diff --git a/nodebuilder/rpc/constructors.go b/nodebuilder/rpc/constructors.go index 43a8055207..6509e38b96 100644 --- a/nodebuilder/rpc/constructors.go +++ b/nodebuilder/rpc/constructors.go @@ -1,7 +1,7 @@ package rpc import ( - "github.com/cristalhq/jwt" + "github.com/cristalhq/jwt/v5" "github.com/celestiaorg/celestia-node/api/rpc" "github.com/celestiaorg/celestia-node/nodebuilder/blob" @@ -39,6 +39,6 @@ func registerEndpoints( serv.RegisterService("da", daMod, &da.API{}) } -func server(cfg *Config, auth jwt.Signer) *rpc.Server { - return rpc.NewServer(cfg.Address, cfg.Port, cfg.SkipAuth, auth) +func server(cfg *Config, signer jwt.Signer, verifier jwt.Verifier) *rpc.Server { + return rpc.NewServer(cfg.Address, cfg.Port, cfg.SkipAuth, signer, verifier) } From 4c037d19d1d879e2463e157845f62a6708a79408 Mon Sep 17 00:00:00 2001 From: Oleg Kovalov Date: Fri, 7 Jun 2024 13:34:33 +0200 Subject: [PATCH 2/6] use proper fx things --- nodebuilder/node/module.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodebuilder/node/module.go b/nodebuilder/node/module.go index 47ce4363ac..ceff5d9b92 100644 --- a/nodebuilder/node/module.go +++ b/nodebuilder/node/module.go @@ -11,6 +11,6 @@ func ConstructModule(tp Type) fx.Option { fx.Provide(func(signer jwt.Signer, verifier jwt.Verifier) Module { return newModule(tp, signer, verifier) }), - fx.Provide(secret), + fx.Provide(jwtSignerAndVerifier), ) } From ebf9ed39b58493149945819b091f98478527bde8 Mon Sep 17 00:00:00 2001 From: Oleg Kovalov Date: Fri, 7 Jun 2024 13:34:38 +0200 Subject: [PATCH 3/6] fix --- nodebuilder/node/auth.go | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/nodebuilder/node/auth.go b/nodebuilder/node/auth.go index 9ae6d29d67..39284dc7d4 100644 --- a/nodebuilder/node/auth.go +++ b/nodebuilder/node/auth.go @@ -11,25 +11,35 @@ import ( var SecretName = keystore.KeyName("jwt-secret.jwt") -// secret returns the node's JWT secret if it exists, or generates -// and saves a new one if it does not. -func secret(ks keystore.Keystore) (jwt.Signer, error) { - // if key already exists, use it - if pk, ok := existing(ks); ok { - return jwt.NewSignerHS(jwt.HS256, pk) +// jwtSignerAndVerifier returns the node's JWT signer and verifier for a saved key, +// or generates and saves a new one if it does not. +func jwtSignerAndVerifier(ks keystore.Keystore) (jwt.Signer, jwt.Verifier, error) { + key, ok := existing(ks) + if !ok { + // otherwise, generate and save new priv key + sk, err := io.ReadAll(io.LimitReader(rand.Reader, 32)) + if err != nil { + return nil, nil, err + } + + // save key + err = ks.Put(SecretName, keystore.PrivKey{Body: sk}) + if err != nil { + return nil, nil, err + } + key = sk } - // otherwise, generate and save new priv key - sk, err := io.ReadAll(io.LimitReader(rand.Reader, 32)) + + signer, err := jwt.NewSignerHS(jwt.HS256, key) if err != nil { - return nil, err + return nil, nil, err } - // save key - err = ks.Put(SecretName, keystore.PrivKey{Body: sk}) + + verifier, err := jwt.NewVerifierHS(jwt.HS256, key) if err != nil { - return nil, err + return nil, nil, err } - - return jwt.NewSignerHS(jwt.HS256, sk) + return signer, verifier, nil } func existing(ks keystore.Keystore) ([]byte, bool) { From 91430da04a69c64a4fddd32993468f92326f1ba4 Mon Sep 17 00:00:00 2001 From: Oleg Kovalov Date: Fri, 7 Jun 2024 14:08:30 +0200 Subject: [PATCH 4/6] fff: fix f-word fx --- api/rpc_test.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/api/rpc_test.go b/api/rpc_test.go index 454dace22a..d56cd523f9 100644 --- a/api/rpc_test.go +++ b/api/rpc_test.go @@ -46,10 +46,15 @@ func TestRPCCallsUnderlyingNode(t *testing.T) { t.Cleanup(cancel) // generate dummy signer and sign admin perms token with it - signer, err := jwt.NewSignerHS(jwt.HS256, make([]byte, 32)) + key := make([]byte, 32) + + signer, err := jwt.NewSignerHS(jwt.HS256, key) + require.NoError(t, err) + + verifier, err := jwt.NewVerifierHS(jwt.HS256, key) require.NoError(t, err) - nd, server := setupNodeWithAuthedRPC(t, signer) + nd, server := setupNodeWithAuthedRPC(t, signer, verifier) url := nd.RPCServer.ListenAddr() adminToken, err := perms.NewTokenWithPerms(signer, perms.AllPerms) @@ -123,10 +128,15 @@ func TestAuthedRPC(t *testing.T) { t.Cleanup(cancel) // generate dummy signer and sign admin perms token with it - signer, err := jwt.NewSignerHS(jwt.HS256, make([]byte, 32)) + key := make([]byte, 32) + + signer, err := jwt.NewSignerHS(jwt.HS256, key) + require.NoError(t, err) + + verifier, err := jwt.NewVerifierHS(jwt.HS256, key) require.NoError(t, err) - nd, server := setupNodeWithAuthedRPC(t, signer) + nd, server := setupNodeWithAuthedRPC(t, signer, verifier) url := nd.RPCServer.ListenAddr() // create permissioned tokens @@ -284,7 +294,7 @@ func implementsMarshaler(t *testing.T, typ reflect.Type) { // setupNodeWithAuthedRPC sets up a node and overrides its JWT // signer with the given signer. -func setupNodeWithAuthedRPC(t *testing.T, auth jwt.Signer) (*nodebuilder.Node, *mockAPI) { +func setupNodeWithAuthedRPC(t *testing.T, jwtSigner jwt.Signer, jwtVerifier jwt.Verifier) (*nodebuilder.Node, *mockAPI) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) @@ -316,8 +326,8 @@ func setupNodeWithAuthedRPC(t *testing.T, auth jwt.Signer) (*nodebuilder.Node, * srv.RegisterService("da", mockAPI.DA, &da.API{}) }) // fx.Replace does not work here, but fx.Decorate does - nd := nodebuilder.TestNode(t, node.Full, invokeRPC, fx.Decorate(func() (jwt.Signer, error) { - return auth, nil + nd := nodebuilder.TestNode(t, node.Full, invokeRPC, fx.Decorate(func() (jwt.Signer, jwt.Verifier, error) { + return jwtSigner, jwtVerifier, nil })) // start node err := nd.Start(ctx) From 1c096983d4035cfe80c5b122f7a06b3dd1d837eb Mon Sep 17 00:00:00 2001 From: Oleg Kovalov Date: Fri, 7 Jun 2024 14:13:56 +0200 Subject: [PATCH 5/6] fix lll --- api/rpc_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/rpc_test.go b/api/rpc_test.go index d56cd523f9..8a0053d22a 100644 --- a/api/rpc_test.go +++ b/api/rpc_test.go @@ -294,7 +294,8 @@ func implementsMarshaler(t *testing.T, typ reflect.Type) { // setupNodeWithAuthedRPC sets up a node and overrides its JWT // signer with the given signer. -func setupNodeWithAuthedRPC(t *testing.T, jwtSigner jwt.Signer, jwtVerifier jwt.Verifier) (*nodebuilder.Node, *mockAPI) { +func setupNodeWithAuthedRPC(t *testing.T, + jwtSigner jwt.Signer, jwtVerifier jwt.Verifier) (*nodebuilder.Node, *mockAPI) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) From 12398d6804bc58546c5a2338edc314454148140a Mon Sep 17 00:00:00 2001 From: Oleg Kovalov Date: Fri, 7 Jun 2024 14:20:12 +0200 Subject: [PATCH 6/6] fix --- api/rpc_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/rpc_test.go b/api/rpc_test.go index 8a0053d22a..eccd84148e 100644 --- a/api/rpc_test.go +++ b/api/rpc_test.go @@ -295,7 +295,8 @@ func implementsMarshaler(t *testing.T, typ reflect.Type) { // setupNodeWithAuthedRPC sets up a node and overrides its JWT // signer with the given signer. func setupNodeWithAuthedRPC(t *testing.T, - jwtSigner jwt.Signer, jwtVerifier jwt.Verifier) (*nodebuilder.Node, *mockAPI) { + jwtSigner jwt.Signer, jwtVerifier jwt.Verifier, +) (*nodebuilder.Node, *mockAPI) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel)