diff --git a/pkg/toolset/jwt.go b/pkg/toolset/jwt.go new file mode 100644 index 000000000..605b58662 --- /dev/null +++ b/pkg/toolset/jwt.go @@ -0,0 +1,58 @@ +package toolset + +import ( + "encoding/hex" + "fmt" + "log" + + "golang.org/x/crypto/blake2b" + + "github.com/iotaledger/hornet/pkg/config" + "github.com/iotaledger/hornet/pkg/jwt" + "github.com/iotaledger/hornet/plugins/webapi" +) + +func generateJWTApiToken(args []string) error { + // get nodes private key + privKey := config.NodeConfig.GetString(config.CfgWebAPIJWTAuthPrivateKey) + privKeyFilePath := config.NodeConfig.GetString(config.CfgWebAPIJWTAuthPrivateKeyPath) + + // load up the previously generated identity or create a new one + jwtPrivateKey, _, err := webapi.LoadOrCreateIdentityPrivateKey(privKeyFilePath, privKey) + if err != nil { + log.Panic(err) + } + + // create an ID by hashing the public key of the JWT private key + jwtPublicKeyBytes, err := jwtPrivateKey.GetPublic().Raw() + if err != nil { + log.Panic(err) + } + jwtIDBytes := blake2b.Sum256(jwtPublicKeyBytes) + jwtID := hex.EncodeToString(jwtIDBytes[:]) + + // configure JWT auth + salt := config.NodeConfig.GetString(config.CfgWebAPIJWTAuthSalt) + if len(salt) == 0 { + log.Fatalf("'%s' should not be empty", config.CfgWebAPIJWTAuthSalt) + } + + // API tokens do not expire. + jwtAuth, err := jwt.NewAuth(salt, + 0, + jwtID, + jwtPrivateKey, + ) + if err != nil { + log.Fatalf("JWT auth initialization failed: %s", err) + } + + jwtToken, err := jwtAuth.IssueJWT() + if err != nil { + return fmt.Errorf("issuing JWT token failed: %w", err) + } + + fmt.Println("Your API JWT token: ", jwtToken) + + return nil +} diff --git a/pkg/toolset/toolset.go b/pkg/toolset/toolset.go index 0a48897e5..954871035 100644 --- a/pkg/toolset/toolset.go +++ b/pkg/toolset/toolset.go @@ -12,6 +12,7 @@ var ( "seedgen": seedGen, "list": listTools, "merkle": merkleTreeCreate, + "jwt-api": generateJWTApiToken, } ) @@ -21,7 +22,7 @@ func HandleTools() { toolFound := false for i, arg := range args { - if strings.ToLower(arg) == "tool" { + if strings.ToLower(arg) == "tool" || strings.ToLower(arg) == "tools" { args = args[i:] toolFound = true break @@ -58,6 +59,7 @@ func listTools(args []string) error { fmt.Println("pwdhash: generates a sha265 sum from your password and salt") fmt.Println("seedgen: generates an autopeering seed") fmt.Println("merkle: generates a Merkle tree for coordinator plugin") + fmt.Println("jwt-api: generates a JWT token for API access") return nil } diff --git a/plugins/webapi/api.go b/plugins/webapi/api.go index 5ba8f26c7..5ae834f55 100644 --- a/plugins/webapi/api.go +++ b/plugins/webapi/api.go @@ -77,7 +77,7 @@ func apiMiddleware() echo.MiddlewareFunc { privKeyFilePath := config.NodeConfig.GetString(config.CfgWebAPIJWTAuthPrivateKeyPath) // load up the previously generated identity or create a new one - jwtPrivateKey, _, err := loadOrCreateIdentityPrivateKey(privKeyFilePath, privKey) + jwtPrivateKey, _, err := LoadOrCreateIdentityPrivateKey(privKeyFilePath, privKey) if err != nil { log.Panic(err) } diff --git a/plugins/webapi/pem.go b/plugins/webapi/pem.go index e3d57bc10..ae0e14f4f 100644 --- a/plugins/webapi/pem.go +++ b/plugins/webapi/pem.go @@ -184,9 +184,9 @@ func writeEd25519PrivateKeyToPEMFile(filepath string, privateKey ed25519.Private return nil } -// loadOrCreateIdentityPrivateKey loads an existing Ed25519 based identity private key +// LoadOrCreateIdentityPrivateKey loads an existing Ed25519 based identity private key // or creates a new one and stores it as a PEM file in the p2p store folder. -func loadOrCreateIdentityPrivateKey(privKeyFilePath string, identityPrivKey string) (libp2pcrypto.PrivKey, bool, error) { +func LoadOrCreateIdentityPrivateKey(privKeyFilePath string, identityPrivKey string) (libp2pcrypto.PrivKey, bool, error) { privKeyFromConfig, err := parseLibp2pEd25519PrivateKeyFromString(identityPrivKey) if err != nil {