diff options
| -rw-r--r-- | auth/auth.go | 29 | ||||
| -rw-r--r-- | auth/client.go | 8 | ||||
| -rw-r--r-- | auth/server.go | 73 | ||||
| -rw-r--r-- | auth/token.go | 70 | 
4 files changed, 158 insertions, 22 deletions
| diff --git a/auth/auth.go b/auth/auth.go index e75d228..a8d28c7 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -21,6 +21,9 @@ package auth  import (    "os"    "os/user" +  "os/exec" +  "runtime" +  "errors"    "fmt"  ) @@ -42,14 +45,36 @@ func Login() {    clientId := askClientId()    challenge := codeChallenge()    link := generateLink(clientId, challenge) -  fmt.Println("Please open this link in the browser:") -  fmt.Println(link) + +  openInBrowser(link) +  listen(clientId, challenge)  }  func generateLink(clientId, challenge string) string {    return "https://myanimelist.net/v1/oauth2/authorize?response_type=code&client_id=" + clientId + "&code_challenge=" + challenge  } +func openInBrowser(url string) { +  fmt.Println("Attempting to launch \x1b[36m" + url + "\x1b[0m in your default web browser. If it doesn't launch please manually copy-paste the link.") + +  var err error +  switch runtime.GOOS { +  case "linux": +    err = exec.Command("xdg-open", url).Start() +  case "windows": +    err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() +  case "darwin": +    err = exec.Command("open", url).Start() +  default: +    err = errors.New("<failed to detect platform>") +  } + +  if err != nil { +    fmt.Println("There was an error while launching your browser.", err) +    fmt.Println("Please manually copy and paste the above URL into your web browser.") +  } +} +  func Logout() {    deleteClientId()    deleteToken() diff --git a/auth/client.go b/auth/client.go index 7268b8f..f266a42 100644 --- a/auth/client.go +++ b/auth/client.go @@ -40,10 +40,12 @@ func setClientId(clientId string) {  func deleteClientId() {    err := keyring.Delete(serviceName + clientSuffix, userName) -  // TODO: if secret doesnt exist dont show error +  // if secret doesnt exist dont show error    if err != nil { -    fmt.Println(err) -    os.Exit(1) +    if err.Error() != "secret not found in keyring" { +      fmt.Println("Error while deleting Client ID", err.Error()) +      os.Exit(1) +    }    }  } diff --git a/auth/server.go b/auth/server.go index eef95bb..5a18a36 100644 --- a/auth/server.go +++ b/auth/server.go @@ -18,18 +18,61 @@ 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) -//   } -// } +import ( +  "net/http" +  "net/url" +  "encoding/json" +  "os" +  "fmt" +	// "io/ioutil" +) + +func listen(clientId, verifier string) { +  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { +    query := r.URL.Query() + +	code, codePresent := query["code"] +	if !codePresent { +	  // TODO: check if error message present +	  fmt.Println("Error: response from MyAnimeList doesn't contain required code.") +	  os.Exit(1) +	} + +	accessToken, refreshToken, expiresIn := requestToken(clientId, verifier, code[0]) + +	if accessToken != "" { +	  w.WriteHeader(200) +	  w.Write([]byte("<h1>You have successfully logged into macli.</h1>")) +	} + +	setToken(accessToken) +	setRefreshToken(refreshToken) +	setExpiresIn(expiresIn) +  }) + +  err := http.ListenAndServe(":8000", nil) +  if err != nil { +    fmt.Println("There was an error initialising the server", err.Error()) +    os.Exit(1) +  } +} + +func requestToken(clientId, verifier, code string) (string, string, string) { +  data := url.Values{ +	"client_id": {clientId}, +	"code_verifier": {verifier}, +	"grant_type": {"authorization_code"}, +	"code": {code}, +  } + +  resp, err := http.PostForm("https://myanimelist.net/v1/oauth2/token", data) +  if err != nil { +    fmt.Println("Error while requesting an access token:", err) +	os.Exit(1) +  } + +  var res map[string]interface{} +  json.NewDecoder(resp.Body).Decode(&res) + +  return fmt.Sprintf("%v", res["access_token"]), fmt.Sprintf("%v", res["refresh_token"]), fmt.Sprintf("%v", res["expires_in"]) +} diff --git a/auth/token.go b/auth/token.go index 5997025..fb1073f 100644 --- a/auth/token.go +++ b/auth/token.go @@ -24,6 +24,9 @@ import (    "github.com/zalando/go-keyring"  ) +var refreshPrefix string = "-refresh-token" +var expiresPrefix string = "-expires-in" +  func GetToken() string {    secret, err := keyring.Get(serviceName, userName)    if err != nil { @@ -45,9 +48,72 @@ func setToken(secret string) {  func deleteToken() {    err := keyring.Delete(serviceName, userName) -  // TODO: if secret doesnt exist dont show error +  // if secret doesnt exist dont show error +  if err != nil { +    if err.Error() != "secret not found in keyring" { +      fmt.Println("Error while deleting authentication token", err.Error()) +      os.Exit(1) +    } +  } +} + +// currently refreshtoken has no use +func setRefreshToken(secret string) { +  err := keyring.Set(serviceName + refreshPrefix, userName, secret) +  if err != nil { +    fmt.Println("Error while writing access token to keychain", err) +    os.Exit(1) +  } +} + +func getRefreshToken() string { +  secret, err := keyring.Get(serviceName + refreshPrefix, userName) +  if err != nil { +    fmt.Println("\x1b[31mError while reading refresh token from keychain:", err.Error(), "\x1b[0m") +    fmt.Println("Your access token won't be automatically refreshed. If you have problems using macli please run `macli login` to log in again.") +    os.Exit(1) +  } + +  return secret +} + +func deleteRefreshToken() { +  err := keyring.Delete(serviceName + refreshPrefix, userName) +  // if secret doesnt exist dont show error +  if err != nil { +    if err.Error() != "secret not found in keyring" { +      fmt.Println("Error while deleting refresh token", err.Error()) +      os.Exit(1) +    } +  } +} + +func setExpiresIn(secret string) { +  err := keyring.Set(serviceName + expiresPrefix, userName, secret)    if err != nil { -    fmt.Println(err) +    fmt.Println("Error while writing token expire time to keychain", err)      os.Exit(1)    }  } + +func getExpiresIn() string { +  secret, err := keyring.Get(serviceName + expiresPrefix, userName) +  if err != nil { +    fmt.Println("\x1b[31mError while reading token expire time from keychain:", err.Error(), "\x1b[0m") +    fmt.Println("Please log in again using `macli login` if problems occour") +    // os.Exit(1) +  } + +  return secret +} + +func deleteExpiresIn() { +  err := keyring.Delete(serviceName + expiresPrefix, userName) +  // if secret doesnt exist dont show error +  if err != nil { +    if err.Error() != "secret not found in keyring" { +      fmt.Println("Error while deleting token expires in data", err.Error()) +      os.Exit(1) +    } +  } +} |