aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--auth/auth.go29
-rw-r--r--auth/challenge.go35
-rw-r--r--auth/client.go67
-rw-r--r--auth/input.go57
-rw-r--r--auth/server.go35
-rw-r--r--auth/token.go53
-rw-r--r--cmd/login.go13
-rw-r--r--cmd/logout.go39
-rw-r--r--ui/input.go30
9 files changed, 301 insertions, 57 deletions
diff --git a/auth/auth.go b/auth/auth.go
index c3b2cfd..e75d228 100644
--- a/auth/auth.go
+++ b/auth/auth.go
@@ -22,7 +22,6 @@ import (
"os"
"os/user"
"fmt"
- "github.com/zalando/go-keyring"
)
var serviceName string = "macli"
@@ -38,22 +37,20 @@ func init() {
userName = currentUser.Username
}
-func Login(secret string) {
- err := keyring.Set(serviceName, userName, secret)
- if err != nil {
- fmt.Println("Error while writing access token to keychain", err)
- os.Exit(1)
- }
+// asks for all the details
+func Login() {
+ clientId := askClientId()
+ challenge := codeChallenge()
+ link := generateLink(clientId, challenge)
+ fmt.Println("Please open this link in the browser:")
+ fmt.Println(link)
}
-func GetToken() string {
- // get mal secret from keyring
- secret, err := keyring.Get(serviceName, userName)
- if err != nil {
- fmt.Println("\x1b[31mError while reading access token from keychain:", err.Error(), "\x1b[0m")
- fmt.Println("Run `macli login` first to authenticate with your MyAnimeList API Token")
- os.Exit(1)
- }
+func generateLink(clientId, challenge string) string {
+ return "https://myanimelist.net/v1/oauth2/authorize?response_type=code&client_id=" + clientId + "&code_challenge=" + challenge
+}
- return secret
+func Logout() {
+ deleteClientId()
+ deleteToken()
}
diff --git a/auth/challenge.go b/auth/challenge.go
new file mode 100644
index 0000000..60161ee
--- /dev/null
+++ b/auth/challenge.go
@@ -0,0 +1,35 @@
+/*
+macli - Unofficial CLI-Based MyAnimeList Client
+Copyright © 2022 Vidhu Kant Sharma <vidhukant@vidhukant.xyz>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package auth
+
+import (
+ "time"
+ "math/rand"
+)
+
+var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
+
+func codeChallenge() string {
+ rand.Seed(time.Now().UnixNano())
+ b := make([]rune, 128)
+ for i := range b {
+ b[i] = letterRunes[rand.Intn(len(letterRunes))]
+ }
+ return string(b)
+}
diff --git a/auth/client.go b/auth/client.go
new file mode 100644
index 0000000..7268b8f
--- /dev/null
+++ b/auth/client.go
@@ -0,0 +1,67 @@
+/*
+macli - Unofficial CLI-Based MyAnimeList Client
+Copyright © 2022 Vidhu Kant Sharma <vidhukant@vidhukant.xyz>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package auth
+
+import (
+ "os"
+ "fmt"
+ "github.com/zalando/go-keyring"
+)
+
+var clientSuffix string = "-client-id"
+
+func getClientId() (string, error) {
+ return keyring.Get(serviceName + clientSuffix, userName)
+}
+
+func setClientId(clientId string) {
+ err := keyring.Set(serviceName + clientSuffix, userName, clientId)
+ if err != nil {
+ fmt.Println("Error while writing Client ID to keychain", err)
+ os.Exit(1)
+ }
+}
+
+func deleteClientId() {
+ err := keyring.Delete(serviceName + clientSuffix, userName)
+ // TODO: if secret doesnt exist dont show error
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+}
+
+// if client id isn't in keyring
+// it will ask the user to enter/create one
+func askClientId() string {
+ clientId, err := getClientId()
+ if err != nil {
+ if err.Error() == "secret not found in keyring" {
+ fmt.Println("Looks like you don't have any Client ID saved.")
+ fmt.Println("If you don't have a MyAnimeList Client ID, please go to \x1b[34mhttps://myanimelist.net/apiconfig\x1b[0m and create one.")
+ fmt.Println("Remember to set the App Redirect Url to \x1b[33mhttp://localhost:8000\x1b[0m. Other details don't matter.")
+
+ // get clientId from user input
+ clientId = secretInput("Enter your Client ID: ", "Client ID Can't be blank")
+ setClientId(clientId)
+ }
+ }
+
+ return clientId
+}
diff --git a/auth/input.go b/auth/input.go
new file mode 100644
index 0000000..9f75fab
--- /dev/null
+++ b/auth/input.go
@@ -0,0 +1,57 @@
+/*
+macli - Unofficial CLI-Based MyAnimeList Client
+Copyright © 2022 Vidhu Kant Sharma <vidhukant@vidhukant.xyz>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package auth
+
+import (
+ "os"
+ "fmt"
+ "errors"
+ p "github.com/manifoldco/promptui"
+)
+
+// because importing macli/ui causes import cycle
+func secretInput(label, errMessage string) string {
+ validate := func(input string) error {
+ if input == "" {
+ return errors.New(errMessage)
+ }
+ return nil
+ }
+
+ template := &p.PromptTemplates {
+ Valid: "{{ . | cyan }}",
+ Invalid: "{{ . | cyan }}",
+ Success: "{{ . | blue }}",
+ }
+
+ prompt := p.Prompt {
+ Label: label,
+ Templates: template,
+ Validate: validate,
+ Mask: '*',
+ }
+
+ res, err := prompt.Run()
+ if err != nil {
+ fmt.Println("Failed to run secret input prompt.", err.Error())
+ os.Exit(1)
+ }
+
+ return res
+}
diff --git a/auth/server.go b/auth/server.go
new file mode 100644
index 0000000..eef95bb
--- /dev/null
+++ b/auth/server.go
@@ -0,0 +1,35 @@
+/*
+macli - Unofficial CLI-Based MyAnimeList Client
+Copyright © 2022 Vidhu Kant Sharma <vidhukant@vidhukant.xyz>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package auth
+
+// import (
+// "net/http"
+// "os"
+// "fmt"
+// )
+//
+// func listen() {
+// http.HandleFunc("/", getRoot)
+//
+// err := http.ListenAndServe(":8000", nil)
+// if err != nil {
+// fmt.Println("There was an error initialising the server", err.Error())
+// os.Exit(1)
+// }
+// }
diff --git a/auth/token.go b/auth/token.go
new file mode 100644
index 0000000..5997025
--- /dev/null
+++ b/auth/token.go
@@ -0,0 +1,53 @@
+/*
+macli - Unofficial CLI-Based MyAnimeList Client
+Copyright © 2022 Vidhu Kant Sharma <vidhukant@vidhukant.xyz>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package auth
+
+import (
+ "os"
+ "fmt"
+ "github.com/zalando/go-keyring"
+)
+
+func GetToken() string {
+ secret, err := keyring.Get(serviceName, userName)
+ if err != nil {
+ fmt.Println("\x1b[31mError while reading access token from keychain:", err.Error(), "\x1b[0m")
+ fmt.Println("Run `macli login` first to authenticate with your MyAnimeList API Token")
+ os.Exit(1)
+ }
+
+ return secret
+}
+
+func setToken(secret string) {
+ err := keyring.Set(serviceName, userName, secret)
+ if err != nil {
+ fmt.Println("Error while writing access token to keychain", err)
+ os.Exit(1)
+ }
+}
+
+func deleteToken() {
+ err := keyring.Delete(serviceName, userName)
+ // TODO: if secret doesnt exist dont show error
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+}
diff --git a/cmd/login.go b/cmd/login.go
index bcecc3f..74a6947 100644
--- a/cmd/login.go
+++ b/cmd/login.go
@@ -20,24 +20,15 @@ package cmd
import (
"github.com/spf13/cobra"
- "github.com/MikunoNaka/macli/ui"
"github.com/MikunoNaka/macli/auth"
)
var loginCmd = &cobra.Command {
Use: "login",
Short: "Login with your MyAnimeList client secret",
- Long: `
-Currently, macli doesn't support logging in.
-You need to manually generate an access token/client secret to authorise
-macli with your MyAnimeList account.
-
-An easy way to generate a token is to use my python script:
-https://github.com/MikunoNaka/mal-authtoken-generator
-`,
+ Long: ``,
Run: func(cmd *cobra.Command, args []string) {
- secret := ui.PasswordInput("Enter your client secret: ", "Client secret can't be empty")
- auth.Login(secret)
+ auth.Login()
},
}
diff --git a/cmd/logout.go b/cmd/logout.go
new file mode 100644
index 0000000..403f6aa
--- /dev/null
+++ b/cmd/logout.go
@@ -0,0 +1,39 @@
+/*
+macli - Unofficial CLI-Based MyAnimeList Client
+Copyright © 2022 Vidhu Kant Sharma <vidhukant@vidhukant.xyz>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+ "github.com/MikunoNaka/macli/auth"
+)
+
+var logoutCmd = &cobra.Command {
+ Use: "logout",
+ Short: "Logout from macli",
+ Long: `Logout from macli
+This will delete the Auth Token and Client ID from system's keyring.
+`,
+ Run: func(cmd *cobra.Command, args []string) {
+ auth.Logout()
+ },
+}
+
+func init() {
+ rootCmd.AddCommand(logoutCmd)
+}
diff --git a/ui/input.go b/ui/input.go
index a334943..d26d2f3 100644
--- a/ui/input.go
+++ b/ui/input.go
@@ -51,33 +51,3 @@ func TextInput(label, errMessage string) string {
return res
}
-
-func PasswordInput(label, errMessage string) string {
- validate := func(input string) error {
- if input == "" {
- return errors.New(errMessage)
- }
- return nil
- }
-
- template := &p.PromptTemplates {
- Valid: "{{ . | cyan }}",
- Invalid: "{{ . | cyan }}",
- Success: "{{ . | blue }}",
- }
-
- prompt := p.Prompt {
- Label: label,
- Templates: template,
- Validate: validate,
- Mask: '*',
- }
-
- res, err := prompt.Run()
- if err != nil {
- fmt.Println("Failed to run input prompt.", err.Error())
- os.Exit(1)
- }
-
- return res
-}