mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Remove JWT Expiration (#9813)
* remove expiry from claims * fix tests * gaz Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
@@ -33,7 +33,6 @@ go_library(
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/validator-client:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//time:go_default_library",
|
||||
"//validator/accounts:go_default_library",
|
||||
"//validator/accounts/iface:go_default_library",
|
||||
"//validator/accounts/petnames:go_default_library",
|
||||
@@ -93,7 +92,6 @@ go_test(
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/mock:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//time:go_default_library",
|
||||
"//validator/accounts:go_default_library",
|
||||
"//validator/accounts/iface:go_default_library",
|
||||
"//validator/accounts/wallet:go_default_library",
|
||||
|
||||
@@ -9,31 +9,24 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/crypto/rand"
|
||||
"github.com/prysmaticlabs/prysm/io/file"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
|
||||
prysmTime "github.com/prysmaticlabs/prysm/time"
|
||||
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
var (
|
||||
tokenExpiryLength = time.Hour
|
||||
)
|
||||
|
||||
const (
|
||||
authTokenFileName = "auth-token"
|
||||
)
|
||||
|
||||
// CreateAuthToken generates a new jwt key, token, and expiration and writes them
|
||||
// CreateAuthToken generates a new jwt key, token and writes them
|
||||
// to a file in the specified directory. Also, it logs out a prepared URL
|
||||
// for the user to navigate to and authenticate with the Prysm web interface.
|
||||
func CreateAuthToken(walletDirPath, validatorWebAddr string) error {
|
||||
@@ -41,16 +34,16 @@ func CreateAuthToken(walletDirPath, validatorWebAddr string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
token, expr, err := createTokenString(jwtKey)
|
||||
token, err := createTokenString(jwtKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authTokenPath := filepath.Join(walletDirPath, authTokenFileName)
|
||||
log.Infof("Generating auth token and saving it to %s", authTokenPath)
|
||||
if err := saveAuthToken(walletDirPath, jwtKey, token, expr); err != nil {
|
||||
if err := saveAuthToken(walletDirPath, jwtKey, token); err != nil {
|
||||
return err
|
||||
}
|
||||
logValidatorWebAuth(validatorWebAddr, token, expr)
|
||||
logValidatorWebAuth(validatorWebAddr, token)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -72,61 +65,51 @@ func (s *Server) Initialize(_ context.Context, _ *emptypb.Empty) (*pb.Initialize
|
||||
// user via stdout and the validator client should then attempt to open the default
|
||||
// browser. The web interface authenticates by looking for this token in the query parameters
|
||||
// of the URL. This token is then used as the bearer token for jwt auth.
|
||||
func (s *Server) initializeAuthToken(walletDir string) (string, uint64, error) {
|
||||
func (s *Server) initializeAuthToken(walletDir string) (string, error) {
|
||||
authTokenFile := filepath.Join(walletDir, authTokenFileName)
|
||||
if file.FileExists(authTokenFile) {
|
||||
// #nosec G304
|
||||
f, err := os.Open(authTokenFile)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
return "", err
|
||||
}
|
||||
r := bufio.NewReader(f)
|
||||
jwtKeyHex, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
return "", err
|
||||
}
|
||||
jwtKey, err := hex.DecodeString(strings.TrimSpace(jwtKeyHex))
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
return "", err
|
||||
}
|
||||
token, err := r.ReadString('\n')
|
||||
token, _, err := r.ReadLine()
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
exprBytes, _, err := r.ReadLine()
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
exprIntStr := strings.TrimSpace(string(exprBytes))
|
||||
expiration, err := strconv.ParseUint(exprIntStr, 10, 64)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
return "", err
|
||||
}
|
||||
s.jwtKey = jwtKey
|
||||
return strings.TrimSpace(token), expiration, nil
|
||||
return strings.TrimSpace(string(token)), nil
|
||||
}
|
||||
jwtKey, err := createRandomJWTSecret()
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
return "", err
|
||||
}
|
||||
s.jwtKey = jwtKey
|
||||
token, expiration, err := createTokenString(s.jwtKey)
|
||||
token, err := createTokenString(s.jwtKey)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
return "", err
|
||||
}
|
||||
if err := saveAuthToken(walletDir, jwtKey, token, expiration); err != nil {
|
||||
return "", 0, err
|
||||
if err := saveAuthToken(walletDir, jwtKey, token); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token, expiration, nil
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func logValidatorWebAuth(validatorWebAddr, token string, expr uint64) {
|
||||
webAuthURLTemplate := "http://%s/initialize?token=%s&expiration=%d"
|
||||
func logValidatorWebAuth(validatorWebAddr, token string) {
|
||||
webAuthURLTemplate := "http://%s/initialize?token=%s"
|
||||
webAuthURL := fmt.Sprintf(
|
||||
webAuthURLTemplate,
|
||||
validatorWebAddr,
|
||||
url.QueryEscape(token),
|
||||
expr,
|
||||
)
|
||||
log.Infof(
|
||||
"Once your validator process is runinng, navigate to the link below to authenticate with " +
|
||||
@@ -135,7 +118,7 @@ func logValidatorWebAuth(validatorWebAddr, token string, expr uint64) {
|
||||
log.Info(webAuthURL)
|
||||
}
|
||||
|
||||
func saveAuthToken(walletDirPath string, jwtKey []byte, token string, expiration uint64) error {
|
||||
func saveAuthToken(walletDirPath string, jwtKey []byte, token string) error {
|
||||
hashFilePath := filepath.Join(walletDirPath, authTokenFileName)
|
||||
bytesBuf := new(bytes.Buffer)
|
||||
if _, err := bytesBuf.Write([]byte(fmt.Sprintf("%x", jwtKey))); err != nil {
|
||||
@@ -150,26 +133,18 @@ func saveAuthToken(walletDirPath string, jwtKey []byte, token string, expiration
|
||||
if _, err := bytesBuf.Write([]byte("\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := bytesBuf.Write([]byte(fmt.Sprintf("%d", expiration))); err != nil {
|
||||
return err
|
||||
}
|
||||
return file.WriteFile(hashFilePath, bytesBuf.Bytes())
|
||||
}
|
||||
|
||||
// Creates a JWT token string using the JWT key with an expiration timestamp.
|
||||
func createTokenString(jwtKey []byte) (string, uint64, error) {
|
||||
// Create a new token object, specifying signing method and the claims
|
||||
// you would like it to contain.
|
||||
expirationTime := prysmTime.Now().Add(tokenExpiryLength)
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
|
||||
ExpiresAt: expirationTime.Unix(),
|
||||
})
|
||||
// Creates a JWT token string using the JWT key.
|
||||
func createTokenString(jwtKey []byte) (string, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{})
|
||||
// Sign and get the complete encoded token as a string using the secret
|
||||
tokenString, err := token.SignedString(jwtKey)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
return "", err
|
||||
}
|
||||
return tokenString, uint64(expirationTime.Unix()), nil
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
func createRandomJWTSecret() ([]byte, error) {
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestServer_AuthenticateUsingExistingToken(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, os.RemoveAll(walletDir))
|
||||
})
|
||||
token, _, err := srv.initializeAuthToken(walletDir)
|
||||
token, err := srv.initializeAuthToken(walletDir)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, len(srv.jwtKey) > 0)
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestServer_AuthenticateUsingExistingToken(t *testing.T) {
|
||||
// Next up, we make the same request but reinitialize the server and we should still
|
||||
// pass with the same auth token.
|
||||
srv = &Server{}
|
||||
_, _, err = srv.initializeAuthToken(walletDir)
|
||||
_, err = srv.initializeAuthToken(walletDir)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, len(srv.jwtKey) > 0)
|
||||
_, err = srv.JWTInterceptor()(ctx, "xyz", unaryInfo, unaryHandler)
|
||||
@@ -62,13 +62,13 @@ func Test_initializeAuthToken(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, os.RemoveAll(walletDir))
|
||||
})
|
||||
token, _, err := srv.initializeAuthToken(walletDir)
|
||||
token, err := srv.initializeAuthToken(walletDir)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, len(srv.jwtKey) > 0)
|
||||
|
||||
// Initializing second time, we generate something from the initial file.
|
||||
srv2 := &Server{}
|
||||
token2, _, err := srv2.initializeAuthToken(walletDir)
|
||||
token2, err := srv2.initializeAuthToken(walletDir)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, bytes.Equal(srv.jwtKey, srv2.jwtKey))
|
||||
require.Equal(t, token, token2)
|
||||
@@ -78,7 +78,7 @@ func Test_initializeAuthToken(t *testing.T) {
|
||||
require.NoError(t, os.RemoveAll(walletDir))
|
||||
srv3 := &Server{}
|
||||
walletDir = setupWalletDir(t)
|
||||
token3, _, err := srv3.initializeAuthToken(walletDir)
|
||||
token3, err := srv3.initializeAuthToken(walletDir)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, len(srv.jwtKey) > 0)
|
||||
require.NotEqual(t, token, token3)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/time"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
@@ -23,7 +22,7 @@ func TestServer_JWTInterceptor_Verify(t *testing.T) {
|
||||
unaryHandler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
token, _, err := createTokenString(s.jwtKey)
|
||||
token, err := createTokenString(s.jwtKey)
|
||||
require.NoError(t, err)
|
||||
ctxMD := map[string][]string{
|
||||
"authorization": {"Bearer " + token},
|
||||
@@ -50,7 +49,7 @@ func TestServer_JWTInterceptor_BadToken(t *testing.T) {
|
||||
badServer := Server{
|
||||
jwtKey: []byte("badTestKey"),
|
||||
}
|
||||
token, _, err := createTokenString(badServer.jwtKey)
|
||||
token, err := createTokenString(badServer.jwtKey)
|
||||
require.NoError(t, err)
|
||||
ctxMD := map[string][]string{
|
||||
"authorization": {"Bearer " + token},
|
||||
@@ -63,11 +62,8 @@ func TestServer_JWTInterceptor_BadToken(t *testing.T) {
|
||||
|
||||
func TestServer_JWTInterceptor_InvalidSigningType(t *testing.T) {
|
||||
ss := &Server{jwtKey: make([]byte, 32)}
|
||||
expirationTime := time.Now().Add(tokenExpiryLength)
|
||||
// Use a different signing type than the expected, HMAC.
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.StandardClaims{
|
||||
ExpiresAt: expirationTime.Unix(),
|
||||
})
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.StandardClaims{})
|
||||
_, err := ss.validateJWT(token)
|
||||
require.ErrorContains(t, "unexpected JWT signing method", err)
|
||||
}
|
||||
|
||||
@@ -189,13 +189,13 @@ func (s *Server) Start() {
|
||||
}
|
||||
}()
|
||||
log.WithField("address", address).Info("gRPC server listening on address")
|
||||
token, expr, err := s.initializeAuthToken(s.walletDir)
|
||||
token, err := s.initializeAuthToken(s.walletDir)
|
||||
if err != nil {
|
||||
log.Errorf("Could not initialize web auth token: %v", err)
|
||||
return
|
||||
}
|
||||
validatorWebAddr := fmt.Sprintf("%s:%d", s.validatorGatewayHost, s.validatorGatewayPort)
|
||||
logValidatorWebAuth(validatorWebAddr, token, expr)
|
||||
logValidatorWebAuth(validatorWebAddr, token)
|
||||
}
|
||||
|
||||
// Stop the gRPC server.
|
||||
|
||||
Reference in New Issue
Block a user