Skip to content

Commit

Permalink
chore: bump cristalhq/jwt to v5 (#3476)
Browse files Browse the repository at this point in the history
  • Loading branch information
cristaloleg committed Jul 3, 2024
1 parent e2b2994 commit accb058
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 57 deletions.
10 changes: 7 additions & 3 deletions api/rpc/perms/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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
}
12 changes: 7 additions & 5 deletions api/rpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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,
Expand All @@ -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{
Expand All @@ -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
Expand Down
28 changes: 20 additions & 8 deletions api/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.NewHS256(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)
Expand Down Expand Up @@ -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.NewHS256(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
Expand Down Expand Up @@ -284,7 +294,9 @@ 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)

Expand Down Expand Up @@ -316,8 +328,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)
Expand Down
4 changes: 2 additions & 2 deletions cmd/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
12 changes: 6 additions & 6 deletions libs/authtoken/authtoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ 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"
)

// 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
}
Expand All @@ -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
}
2 changes: 1 addition & 1 deletion nodebuilder/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
16 changes: 9 additions & 7 deletions nodebuilder/node/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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,
}
}

Expand Down Expand Up @@ -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) {
Expand Down
40 changes: 25 additions & 15 deletions nodebuilder/node/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,42 @@ import (
"crypto/rand"
"io"

"github.com/cristalhq/jwt"
"github.com/cristalhq/jwt/v5"

"github.com/celestiaorg/celestia-node/libs/keystore"
)

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.NewHS256(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.NewHS256(sk)
return signer, verifier, nil
}

func existing(ks keystore.Keystore) ([]byte, bool) {
Expand Down
8 changes: 4 additions & 4 deletions nodebuilder/node/module.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
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),
fx.Provide(jwtSignerAndVerifier),
)
}
6 changes: 3 additions & 3 deletions nodebuilder/rpc/constructors.go
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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)
}

0 comments on commit accb058

Please sign in to comment.