Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: bump cristalhq/jwt to v5 #3476

Merged
merged 7 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
27 changes: 19 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,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, 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 +327,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)
cristaloleg marked this conversation as resolved.
Show resolved Hide resolved
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
cristaloleg marked this conversation as resolved.
Show resolved Hide resolved
}
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)
}
Loading