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:
Raul Jordan
2021-10-26 05:24:09 -05:00
committed by GitHub
parent 28f50862cb
commit 4c677e7b40
5 changed files with 35 additions and 66 deletions

View File

@@ -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",

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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.