From 4d6a7344c6b8433456aa7e69cb690f41c0f3dbd1 Mon Sep 17 00:00:00 2001 From: Elliot Harris Date: Wed, 8 Sep 2021 13:32:24 +0100 Subject: [PATCH 1/6] Fix expired tokens not refreshing. Add 'id' command to show user-id, as well as log it during authentication. --- cmd/common/config.go | 56 +++++++++++++++++++++++++-------------- cmd/root.go | 1 + cmd/users/authenticate.go | 8 ++++-- cmd/users/showid.go | 22 +++++++++++++++ 4 files changed, 65 insertions(+), 22 deletions(-) create mode 100644 cmd/users/showid.go diff --git a/cmd/common/config.go b/cmd/common/config.go index 9f66df9..625601b 100644 --- a/cmd/common/config.go +++ b/cmd/common/config.go @@ -76,6 +76,34 @@ func InitConfig() { } } +// DecodeUserID parses the user access token to get out the "prvd"->"user_id" field. +// Requires the user access token be setup already (i.e. authenticate has been called) +func DecodeUserID() string { + rawToken := RequireUserAccessToken() + + var jwtParser jwt.Parser + token, _, err := jwtParser.ParseUnverified(rawToken, jwt.MapClaims{}) + if err != nil { + log.Printf("failed to parse JWT token on behalf of authorized user; %s", err.Error()) + os.Exit(1) + } + + claims := token.Claims.(jwt.MapClaims) + prvd := claims["prvd"] + if prvd == nil { + log.Printf("failed to get 'prvd' field from token") + os.Exit(1) + } + + if userID, ok := prvd.(map[string]interface{})["user_id"].(string); ok { + return userID + } + + log.Printf("failed to get 'user_id' field from token") + os.Exit(1) + return "" +} + func RequireUserAccessToken() string { token := "" if viper.IsSet(AccessTokenConfigKey) { @@ -217,28 +245,16 @@ func BuildConfigKeyWithUser(keyPartial, userID string) string { } func isTokenExpired(bearerToken string) bool { - token, err := jwt.Parse(bearerToken, func(_jwtToken *jwt.Token) (interface{}, error) { - // uncomment when enabling local verification - // var kid *string - // if kidhdr, ok := _jwtToken.Header["kid"].(string); ok { - // kid = &kidhdr - // } - - // publicKey, _, _, _ := util.ResolveJWTKeypair(kid) - // if publicKey == nil { - // msg := "failed to resolve a valid JWT verification key" - // if kid != nil { - // msg = fmt.Sprintf("%s; invalid kid specified in header: %s", msg, *kid) - // } else { - // msg = fmt.Sprintf("%s; no default verification key configured", msg) - // } - // return nil, fmt.Errorf(msg) - // } - - return nil, nil - }) + + var jwtParser jwt.Parser + token, _, err := jwtParser.ParseUnverified(bearerToken, jwt.MapClaims{}) + if err != nil { + log.Printf("failed to parse JWT token on behalf of authorized user; %s", err.Error()) + os.Exit(1) + } if err != nil { + log.Printf("isTokenExpired err: %s", err) return false } diff --git a/cmd/root.go b/cmd/root.go index 3a940b5..96264a3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -51,6 +51,7 @@ func init() { rootCmd.AddCommand(api_tokens.APITokensCmd) rootCmd.AddCommand(applications.ApplicationsCmd) rootCmd.AddCommand(users.AuthenticateCmd) + rootCmd.AddCommand(users.ShowIDCmd) rootCmd.AddCommand(baseledger.BaseledgerCmd) rootCmd.AddCommand(baseline.BaselineCmd) rootCmd.AddCommand(connectors.ConnectorsCmd) diff --git a/cmd/users/authenticate.go b/cmd/users/authenticate.go index 8f965ed..2447a06 100644 --- a/cmd/users/authenticate.go +++ b/cmd/users/authenticate.go @@ -11,7 +11,7 @@ import ( "github.com/spf13/viper" ) -// authenticateCmd represents the authenticate command +// AuthenticateCmd represents the authenticate command var AuthenticateCmd = &cobra.Command{ Use: "authenticate", Short: "Authenticate using your credentials", @@ -34,9 +34,13 @@ func authenticate(cmd *cobra.Command, args []string) { common.CacheAccessRefreshToken(resp.Token) } else if resp.Token.Token != nil { cacheAPIToken(*resp.Token.Token) + } else { + log.Println("Failed to get token from authentication response.") + os.Exit(1) } - log.Printf("Authentication successful") + log.Print("Authentication successful") + log.Printf("User ID: %s", common.DecodeUserID()) } func cacheAPIToken(token string) { diff --git a/cmd/users/showid.go b/cmd/users/showid.go new file mode 100644 index 0000000..33059b0 --- /dev/null +++ b/cmd/users/showid.go @@ -0,0 +1,22 @@ +package users + +import ( + "fmt" + + "github.com/provideplatform/provide-cli/cmd/common" + + "github.com/spf13/cobra" +) + +// ShowIDCmd represents the id command +var ShowIDCmd = &cobra.Command{ + Use: "id", + Short: "Prints out the ID of the currently authenticated user", + Long: "Prints out the ID of the currently authenticated user", + Run: showid, +} + +func showid(cmd *cobra.Command, args []string) { + id := common.DecodeUserID() + fmt.Println(id) +} From 1fea4005a1a7cb6728771bc06770e6b942129560 Mon Sep 17 00:00:00 2001 From: Elliot Harris Date: Wed, 8 Sep 2021 14:33:22 +0100 Subject: [PATCH 2/6] Move from 'id' to 'users id' and add to 'users' prompt --- cmd/root.go | 1 - cmd/users/showid.go | 2 +- cmd/users/users.go | 1 + cmd/users/users_prompt.go | 5 ++++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 96264a3..3a940b5 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -51,7 +51,6 @@ func init() { rootCmd.AddCommand(api_tokens.APITokensCmd) rootCmd.AddCommand(applications.ApplicationsCmd) rootCmd.AddCommand(users.AuthenticateCmd) - rootCmd.AddCommand(users.ShowIDCmd) rootCmd.AddCommand(baseledger.BaseledgerCmd) rootCmd.AddCommand(baseline.BaselineCmd) rootCmd.AddCommand(connectors.ConnectorsCmd) diff --git a/cmd/users/showid.go b/cmd/users/showid.go index 33059b0..0263add 100644 --- a/cmd/users/showid.go +++ b/cmd/users/showid.go @@ -9,7 +9,7 @@ import ( ) // ShowIDCmd represents the id command -var ShowIDCmd = &cobra.Command{ +var showIDCmd = &cobra.Command{ Use: "id", Short: "Prints out the ID of the currently authenticated user", Long: "Prints out the ID of the currently authenticated user", diff --git a/cmd/users/users.go b/cmd/users/users.go index 9f150c8..f962331 100644 --- a/cmd/users/users.go +++ b/cmd/users/users.go @@ -25,4 +25,5 @@ var UsersCmd = &cobra.Command{ func init() { UsersCmd.AddCommand(createCmd) + UsersCmd.AddCommand(showIDCmd) } diff --git a/cmd/users/users_prompt.go b/cmd/users/users_prompt.go index a931111..01c43f2 100644 --- a/cmd/users/users_prompt.go +++ b/cmd/users/users_prompt.go @@ -7,8 +7,9 @@ import ( var promptStepCreate = "Create" var promptStepAuthenticate = "Authenticate" +var promptStepShowID = "Show current authorized user's ID" -var emptyPromptArgs = []string{promptStepCreate, promptStepAuthenticate} +var emptyPromptArgs = []string{promptStepCreate, promptStepAuthenticate, promptStepShowID} var emptyPromptLabel = "What would you like to do" // General Endpoints @@ -18,6 +19,8 @@ func generalPrompt(cmd *cobra.Command, args []string, currentStep string) { authenticate(cmd, args) case promptStepCreate: create(cmd, args) + case promptStepShowID: + showid(cmd, args) case "": result := common.SelectInput(emptyPromptArgs, emptyPromptLabel) generalPrompt(cmd, args, result) From 6c127743b5d7f448983a2a1c84312119a609193d Mon Sep 17 00:00:00 2001 From: Elliot Harris Date: Wed, 8 Sep 2021 17:19:41 +0100 Subject: [PATCH 3/6] Refactor into 'info' command --- cmd/common/config.go | 42 +++++++++++++++++++------------------ cmd/info/info.go | 44 +++++++++++++++++++++++++++++++++++++++ cmd/root.go | 2 ++ cmd/users/authenticate.go | 6 +++++- cmd/users/showid.go | 22 -------------------- cmd/users/users.go | 1 - cmd/users/users_prompt.go | 5 +---- 7 files changed, 74 insertions(+), 48 deletions(-) create mode 100644 cmd/info/info.go delete mode 100644 cmd/users/showid.go diff --git a/cmd/common/config.go b/cmd/common/config.go index 625601b..cb72160 100644 --- a/cmd/common/config.go +++ b/cmd/common/config.go @@ -1,6 +1,7 @@ package common import ( + "encoding/json" "fmt" "log" "os" @@ -30,6 +31,7 @@ const ( AccountConfigKeyPartial = "account" // app-scoped account ID key OrganizationConfigKeyPartial = "organization" // app-scoped organization ID key WalletConfigKeyPartial = "wallet" // app-scoped HD wallet ID key + UserInfoConfigKey = "prvd-user-info" // details of the currently auth'd user ) var CfgFile string @@ -76,32 +78,32 @@ func InitConfig() { } } -// DecodeUserID parses the user access token to get out the "prvd"->"user_id" field. -// Requires the user access token be setup already (i.e. authenticate has been called) -func DecodeUserID() string { - rawToken := RequireUserAccessToken() - - var jwtParser jwt.Parser - token, _, err := jwtParser.ParseUnverified(rawToken, jwt.MapClaims{}) +func StoreUserDetails(user *ident.User) error { + json, err := json.Marshal(user) if err != nil { - log.Printf("failed to parse JWT token on behalf of authorized user; %s", err.Error()) - os.Exit(1) + log.Printf("Error storing user details; %s", err) + return err } - claims := token.Claims.(jwt.MapClaims) - prvd := claims["prvd"] - if prvd == nil { - log.Printf("failed to get 'prvd' field from token") - os.Exit(1) - } + viper.Set(UserInfoConfigKey, string(json)) + viper.WriteConfig() + + return nil +} + +func GetUserDetails() (*ident.User, error) { + if viper.IsSet(UserInfoConfigKey) { + raw := viper.GetString(UserInfoConfigKey) + var user ident.User + err := json.Unmarshal([]byte(raw), &user) + if err != nil { + return nil, err + } - if userID, ok := prvd.(map[string]interface{})["user_id"].(string); ok { - return userID + return &user, nil } - log.Printf("failed to get 'user_id' field from token") - os.Exit(1) - return "" + return nil, fmt.Errorf("no user details stored, please authorize") } func RequireUserAccessToken() string { diff --git a/cmd/info/info.go b/cmd/info/info.go new file mode 100644 index 0000000..7b35588 --- /dev/null +++ b/cmd/info/info.go @@ -0,0 +1,44 @@ +package info + +import ( + "fmt" + "log" + "os" + + "github.com/provideplatform/provide-cli/cmd/common" + "github.com/spf13/cobra" +) + +const contractTypeRegistry = "registry" + +var contract map[string]interface{} +var contracts []interface{} +var contractType string + +// InfoCmd is the handler for the `info` command +var InfoCmd = &cobra.Command{ + Use: "info", + Short: "Get information about the currently authorized user", + Long: "Get information about the currently authorized user", + Run: showInfo, +} + +func showInfo(cmd *cobra.Command, args []string) { + // User ID, email, username, .. + + user, err := common.GetUserDetails() + + if err != nil { + log.Printf("Unable to get user details: %s", err) + os.Exit(1) + } + + if user != nil { + fmt.Println("Current User:") + fmt.Println(" ID: ", user.ID) + fmt.Println(" Name: ", user.Name) + fmt.Println(" First Name: ", user.FirstName) + fmt.Println(" Last Name: ", user.LastName) + fmt.Println(" Email: ", user.Email) + } +} diff --git a/cmd/root.go b/cmd/root.go index 3a940b5..135fa1b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -14,6 +14,7 @@ import ( "github.com/provideplatform/provide-cli/cmd/common" "github.com/provideplatform/provide-cli/cmd/connectors" "github.com/provideplatform/provide-cli/cmd/contracts" + "github.com/provideplatform/provide-cli/cmd/info" "github.com/provideplatform/provide-cli/cmd/networks" "github.com/provideplatform/provide-cli/cmd/nodes" "github.com/provideplatform/provide-cli/cmd/organizations" @@ -62,6 +63,7 @@ func init() { rootCmd.AddCommand(users.UsersCmd) rootCmd.AddCommand(vaults.VaultsCmd) rootCmd.AddCommand(wallets.WalletsCmd) + rootCmd.AddCommand(info.InfoCmd) common.CacheCommands(rootCmd) } diff --git a/cmd/users/authenticate.go b/cmd/users/authenticate.go index 2447a06..c61e075 100644 --- a/cmd/users/authenticate.go +++ b/cmd/users/authenticate.go @@ -40,7 +40,11 @@ func authenticate(cmd *cobra.Command, args []string) { } log.Print("Authentication successful") - log.Printf("User ID: %s", common.DecodeUserID()) + + if resp.User != nil { + common.StoreUserDetails(resp.User) + log.Printf("User ID: %s", resp.User.ID) + } } func cacheAPIToken(token string) { diff --git a/cmd/users/showid.go b/cmd/users/showid.go deleted file mode 100644 index 0263add..0000000 --- a/cmd/users/showid.go +++ /dev/null @@ -1,22 +0,0 @@ -package users - -import ( - "fmt" - - "github.com/provideplatform/provide-cli/cmd/common" - - "github.com/spf13/cobra" -) - -// ShowIDCmd represents the id command -var showIDCmd = &cobra.Command{ - Use: "id", - Short: "Prints out the ID of the currently authenticated user", - Long: "Prints out the ID of the currently authenticated user", - Run: showid, -} - -func showid(cmd *cobra.Command, args []string) { - id := common.DecodeUserID() - fmt.Println(id) -} diff --git a/cmd/users/users.go b/cmd/users/users.go index f962331..9f150c8 100644 --- a/cmd/users/users.go +++ b/cmd/users/users.go @@ -25,5 +25,4 @@ var UsersCmd = &cobra.Command{ func init() { UsersCmd.AddCommand(createCmd) - UsersCmd.AddCommand(showIDCmd) } diff --git a/cmd/users/users_prompt.go b/cmd/users/users_prompt.go index 01c43f2..a931111 100644 --- a/cmd/users/users_prompt.go +++ b/cmd/users/users_prompt.go @@ -7,9 +7,8 @@ import ( var promptStepCreate = "Create" var promptStepAuthenticate = "Authenticate" -var promptStepShowID = "Show current authorized user's ID" -var emptyPromptArgs = []string{promptStepCreate, promptStepAuthenticate, promptStepShowID} +var emptyPromptArgs = []string{promptStepCreate, promptStepAuthenticate} var emptyPromptLabel = "What would you like to do" // General Endpoints @@ -19,8 +18,6 @@ func generalPrompt(cmd *cobra.Command, args []string, currentStep string) { authenticate(cmd, args) case promptStepCreate: create(cmd, args) - case promptStepShowID: - showid(cmd, args) case "": result := common.SelectInput(emptyPromptArgs, emptyPromptLabel) generalPrompt(cmd, args, result) From 88a6b991dec53aceed5b20bdcc2d552a37a70fcb Mon Sep 17 00:00:00 2001 From: Elliot Harris Date: Wed, 8 Sep 2021 17:30:30 +0100 Subject: [PATCH 4/6] Fix wording of unauth error --- cmd/common/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/common/config.go b/cmd/common/config.go index cb72160..7f130e8 100644 --- a/cmd/common/config.go +++ b/cmd/common/config.go @@ -103,7 +103,7 @@ func GetUserDetails() (*ident.User, error) { return &user, nil } - return nil, fmt.Errorf("no user details stored, please authorize") + return nil, fmt.Errorf("no user details stored, please authenticate") } func RequireUserAccessToken() string { From 92e893bfb864bac5ce7d1694ab6dd6eee526b4e9 Mon Sep 17 00:00:00 2001 From: Elliot Harris Date: Thu, 9 Sep 2021 13:31:34 +0100 Subject: [PATCH 5/6] Reword error message when not authenticated --- cmd/common/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/common/config.go b/cmd/common/config.go index 7f130e8..47a4f53 100644 --- a/cmd/common/config.go +++ b/cmd/common/config.go @@ -103,7 +103,7 @@ func GetUserDetails() (*ident.User, error) { return &user, nil } - return nil, fmt.Errorf("no user details stored, please authenticate") + return nil, fmt.Errorf("please authenticate to retrieve your user info") } func RequireUserAccessToken() string { From 09562e04c41cc92aa849ebdcf644959cb51e1653 Mon Sep 17 00:00:00 2001 From: Elliot Harris Date: Fri, 8 Oct 2021 18:50:16 +0100 Subject: [PATCH 6/6] remove redundant error log in token check --- cmd/common/config.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmd/common/config.go b/cmd/common/config.go index 47a4f53..967aad7 100644 --- a/cmd/common/config.go +++ b/cmd/common/config.go @@ -247,7 +247,6 @@ func BuildConfigKeyWithUser(keyPartial, userID string) string { } func isTokenExpired(bearerToken string) bool { - var jwtParser jwt.Parser token, _, err := jwtParser.ParseUnverified(bearerToken, jwt.MapClaims{}) if err != nil { @@ -255,11 +254,6 @@ func isTokenExpired(bearerToken string) bool { os.Exit(1) } - if err != nil { - log.Printf("isTokenExpired err: %s", err) - return false - } - claims := token.Claims.(jwt.MapClaims) if exp, expOk := claims["exp"].(int64); expOk { expTime := time.Unix(exp, 0)