diff --git a/cmd/akash/cmd/auth.go b/cmd/akash/cmd/auth.go new file mode 100644 index 0000000000..d6e066c7cb --- /dev/null +++ b/cmd/akash/cmd/auth.go @@ -0,0 +1,165 @@ +package cmd + +import ( + "fmt" + "strconv" + "time" + + "github.com/golang-jwt/jwt/v5" + "github.com/spf13/cobra" + + sdkclient "github.com/cosmos/cosmos-sdk/client" + + ajwt "github.com/akash-network/akash-api/go/util/jwt" +) + +const ( + FlagJWTExp = "exp" + FlagJWTNbf = "nbf" + FlagJWTAccess = "access" + FlagJWTScope = "scope" +) + +func AuthCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "auth", + } + + cmd.AddCommand(authJWTCmd()) + + return cmd +} + +func authJWTCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "jwt", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + cctx, err := sdkclient.GetClientTxContext(cmd) + if err != nil { + return err + } + + signer := ajwt.NewSigner(cctx.Keyring, cctx.FromAddress) + + now := time.Now() + nbf := now + + expString, err := cmd.Flags().GetString(FlagJWTExp) + if err != nil { + return err + } + + var exp time.Time + // first, attempt to parse expiration value as duration. + // fallback to unix timestamp if fails + dur, err := time.ParseDuration(expString) + if err != nil { + expInt, err := strconv.ParseInt(expString, 10, 64) + if err != nil { + return err + } + + exp = time.Unix(expInt, 0) + } else { + exp = now.Add(dur) + } + + if cmd.Flags().Changed(FlagJWTNbf) { + nbfString, err := cmd.Flags().GetString(FlagJWTNbf) + if err != nil { + return err + } + + // first, attempt to parse expiration value as duration. + // fallback to unix timestamp if fails + dur, err := time.ParseDuration(nbfString) + if err != nil { + nbfInt, err := strconv.ParseInt(nbfString, 10, 64) + if err != nil { + return err + } + + exp = time.Unix(nbfInt, 0) + } else { + exp = now.Add(dur) + } + } + + accessString, err := cmd.Flags().GetString(FlagJWTAccess) + if err != nil { + return err + } + + access, valid := parseAccess(accessString) + if !valid { + return fmt.Errorf("invalid `access` flags") + } + + var scope ajwt.PermissionScopes + + if cmd.Flags().Changed(FlagJWTScope) { + scopeString, err := cmd.Flags().GetString(FlagJWTAccess) + if err != nil { + return err + } + + if err = scope.UnmarshalCSV(scopeString); err != nil { + return err + } + } + + if !exp.After(now) { + return fmt.Errorf("`exp` value is invalid or in the past. expected %d (exp) > %d (curr)", exp.Unix(), now.Unix()) + } + + if !nbf.After(exp) { + return fmt.Errorf("`nbf` value is invalid. expected %d (nbf) < %d (exp)", nbf.Unix(), exp.Unix()) + } + + claims := ajwt.Claims{ + RegisteredClaims: jwt.RegisteredClaims{ + Issuer: cctx.FromAddress.String(), + IssuedAt: jwt.NewNumericDate(now), + NotBefore: jwt.NewNumericDate(nbf), + ExpiresAt: jwt.NewNumericDate(exp), + }, + Version: "v1", + Leases: ajwt.Leases{ + Access: access, + Scope: scope, + }, + } + + tok := jwt.NewWithClaims(ajwt.SigningMethodES256K, &claims) + + tokString, err := tok.SignedString(signer) + if err != nil { + return err + } + + return cctx.PrintString(tokString) + }, + } + + cmd.Flags().String(FlagJWTExp, "15m", "Set token's `exp` field. Format is either 15m|h or unix timestamp") + cmd.Flags().String(FlagJWTNbf, "", "Set token's `nbf` field. Format is either 15m|h or unix timestamp. Empty equals to current timestamp") + cmd.Flags().String(FlagJWTAccess, "full", "Set token's `leases.access` field. Permitted values are full|scoped|granular. Default is full") + cmd.Flags().StringSlice(FlagJWTScope, nil, "Set token's `leases.scope` field. Comma separated list of scopes. Can only be set if `leases.access=scoped`. Allowed scopes are") + + return cmd +} + +func parseAccess(val string) (ajwt.AccessType, bool) { + res := ajwt.AccessType(val) + + switch res { + case ajwt.AccessTypeFull: + case ajwt.AccessTypeScoped: + case ajwt.AccessTypeGranular: + default: + return ajwt.AccessTypeNone, false + } + + return res, true +} diff --git a/cmd/akash/cmd/root.go b/cmd/akash/cmd/root.go index 3971f12a57..25d5ddd160 100644 --- a/cmd/akash/cmd/root.go +++ b/cmd/akash/cmd/root.go @@ -145,6 +145,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { ecmd.EventCmd(), QueryCmd(), TxCmd(), + AuthCmd(), keys.Commands(app.DefaultHome), genutilcli.InitCmd(app.ModuleBasics(), app.DefaultHome), genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, app.DefaultHome), diff --git a/go.mod b/go.mod index c6cfb2ae63..3d0e8433a1 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.0 toolchain go1.23.6 require ( - github.com/akash-network/akash-api v0.0.78 + github.com/akash-network/akash-api v0.0.80 github.com/blang/semver/v4 v4.0.0 github.com/boz/go-lifecycle v0.1.1 github.com/cosmos/cosmos-sdk v0.45.16 @@ -77,6 +77,8 @@ replace ( google.golang.org/grpc => google.golang.org/grpc v1.33.2 ) +require github.com/golang-jwt/jwt/v5 v5.2.2 + require ( cosmossdk.io/api v0.2.6 // indirect cosmossdk.io/core v0.5.1 // indirect @@ -189,6 +191,9 @@ require ( github.com/tendermint/go-amino v0.16.0 // indirect github.com/tidwall/btree v1.5.0 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.14.1 // indirect go.etcd.io/bbolt v1.3.6 // indirect diff --git a/go.sum b/go.sum index 6d81d5daec..3434735603 100644 --- a/go.sum +++ b/go.sum @@ -76,8 +76,8 @@ github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBA github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/akash-network/akash-api v0.0.78 h1:X75JgjjjL3B+nZjWWEjSq8SXRUPM2r59x/Wc0wjOx1g= -github.com/akash-network/akash-api v0.0.78/go.mod h1:SpY0xGhmCu6Nz/M5MsKIyJUwzw2FBKR3yAeawUcBlG4= +github.com/akash-network/akash-api v0.0.80 h1:qbdhk7jdd0YsnmyOs7y+lyRKfOUm3MUhM+qBkMwL89Q= +github.com/akash-network/akash-api v0.0.80/go.mod h1:SpY0xGhmCu6Nz/M5MsKIyJUwzw2FBKR3yAeawUcBlG4= github.com/akash-network/cometbft v0.34.27-akash.3 h1:ObmkKrMybIuRLPcwPwUMJ8Pllsr+Gsve443mkJsonMA= github.com/akash-network/cometbft v0.34.27-akash.3/go.mod h1:BcCbhKv7ieM0KEddnYXvQZR+pZykTKReJJYf7YC7qhw= github.com/akash-network/cosmos-sdk v0.45.16-akash.3 h1:QiHOQ1ACzCvAEXRlzGNQhp9quWLOowE104D0uESGrEk= @@ -411,6 +411,8 @@ github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -1030,8 +1032,11 @@ github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=