From 66b67c5525cd3022cce82c9224cae352496bbc37 Mon Sep 17 00:00:00 2001 From: Matteo Suppo Date: Mon, 18 May 2020 17:18:04 +0200 Subject: [PATCH 1/5] Perform a device authentication flow with auth0 --- .golangci.yml | 2 +- auth/auth.go | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++ install.go | 43 +++++++++++++++++++++++++-- main.go | 8 ++--- 4 files changed, 126 insertions(+), 8 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 1525df65..ad57114a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -45,7 +45,7 @@ run: - validate.go linters: - enable-all: true + enable-all: false disable: - prealloc - dupl \ No newline at end of file diff --git a/auth/auth.go b/auth/auth.go index 615b2077..cc748d39 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -44,6 +44,87 @@ import ( "github.com/pkg/errors" ) +type DeviceCode struct { + DeviceCode string `json:"device_code"` + UserCode string `json:"user_code"` + VerificationURI string `json:"verification_uri"` + ExpiresIn int `json:"expires_in"` + Interval int `json:"interval"` + VerificationURIComplete string `json:"verification_uri_complete"` +} + +func StartDeviceAuth(authURL string) (data DeviceCode, err error) { + url := authURL + "/oauth/device/code" + + payload := strings.NewReader("client_id=ks1R298bA8IQnG4p6dPlbdEIXF6Kt1Lu&audience=https://api.arduino.cc") + + req, err := http.NewRequest("POST", url, payload) + if err != nil { + return data, err + } + + req.Header.Add("content-type", "application/x-www-form-urlencoded") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return data, err + } + + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return data, err + } + + err = json.Unmarshal(body, &data) + if err != nil { + return data, err + } + + return data, nil +} + +func CheckDeviceAuth(authURL, deviceCode string) (token string, err error) { + url := authURL + "/oauth/token" + + payload := strings.NewReader("grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code&device_code=" + deviceCode + "&client_id=ks1R298bA8IQnG4p6dPlbdEIXF6Kt1Lu") + + req, err := http.NewRequest("POST", url, payload) + if err != nil { + return token, err + } + + req.Header.Add("content-type", "application/x-www-form-urlencoded") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return token, err + } + + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return token, err + } + + if res.StatusCode != 200 { + return token, errors.New(string(body)) + } + + data := struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + TokenType string `json:"token_type"` + }{} + + err = json.Unmarshal(body, &data) + if err != nil { + return token, err + } + + return data.AccessToken, nil +} + // Config contains the variables you may want to change type Config struct { // CodeURL is the endpoint to redirect to obtain a code diff --git a/install.go b/install.go index d197a4b0..e2fb16bb 100644 --- a/install.go +++ b/install.go @@ -20,6 +20,7 @@ package main import ( "bytes" + "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -40,7 +41,7 @@ import ( "time" "github.com/arduino/arduino-connector/auth" - "github.com/eclipse/paho.mqtt.golang" + mqtt "github.com/eclipse/paho.mqtt.golang" "github.com/facchinm/service" "github.com/kardianos/osext" "github.com/pkg/errors" @@ -64,10 +65,12 @@ func register(config Config, configFile, token string) { // Request token var err error if token == "" { - token, err = askCredentials(config.AuthURL) - check(err, "AskCredentials") + token, err = deviceAuth(config.AuthURL) + check(err, "deviceAuth") } + fmt.Println(token) + // Generate a Private Key and CSR csr := generateKeyAndCsr(config) @@ -142,6 +145,40 @@ func registerDeviceViaMQTT(config Config) { } +// Implements Auth0 device authentication flow: https://auth0.com/docs/flows/guides/device-auth/call-api-device-auth +func deviceAuth(authURL string) (token string, err error) { + code, err := auth.StartDeviceAuth(authURL) + if err != nil { + return "", err + } + + fmt.Printf("Go to %s and confirm authentication\n", code.VerificationURIComplete) + + ticker := time.NewTicker(10 * time.Second) + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + + // Loop until the user authenticated or the timeout hits +Loop: + for { + select { + case <-ctx.Done(): + break Loop + case <-ticker.C: + var err error + token, err = auth.CheckDeviceAuth(authURL, code.DeviceCode) + if err == nil { + cancel() + } + } + } + + ticker.Stop() + cancel() + + return token, nil +} + func askCredentials(authURL string) (token string, err error) { var user, pass string fmt.Println("Insert your arduino username") diff --git a/main.go b/main.go index 8e2721dc..485a636a 100644 --- a/main.go +++ b/main.go @@ -31,7 +31,7 @@ import ( "time" docker "github.com/docker/docker/client" - "github.com/eclipse/paho.mqtt.golang" + mqtt "github.com/eclipse/paho.mqtt.golang" "github.com/fsnotify/fsnotify" "github.com/hpcloud/tail" "github.com/namsral/flag" @@ -108,8 +108,8 @@ func main() { flag.StringVar(&config.HTTPProxy, "http_proxy", "", "URL of HTTP proxy to use") flag.StringVar(&config.HTTPSProxy, "https_proxy", "", "URL of HTTPS proxy to use") flag.StringVar(&config.ALLProxy, "all_proxy", "", "URL of SOCKS proxy to use") - flag.StringVar(&config.AuthURL, "authurl", "https://hydra.arduino.cc", "Url of authentication server") - flag.StringVar(&config.APIURL, "apiurl", "https://api2.arduino.cc", "Url of api server") + flag.StringVar(&config.AuthURL, "authurl", "https://login.oniudra.cc", "Url of authentication server") + flag.StringVar(&config.APIURL, "apiurl", "https://api-dev.arduino.cc", "Url of api server") flag.BoolVar(&config.CheckRoFs, "check_ro_fs", false, "Check for Read Only file system and remount if necessary") flag.BoolVar(&debugMqtt, "debug-mqtt", false, "Output all received/sent messages") flag.StringVar(&config.SignatureKey, "signature_key", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvc0yZr1yUSen7qmE3cxF\nIE12rCksDnqR+Hp7o0nGi9123eCSFcJ7CkIRC8F+8JMhgI3zNqn4cUEn47I3RKD1\nZChPUCMiJCvbLbloxfdJrUi7gcSgUXrlKQStOKF5Iz7xv1M4XOP3JtjXLGo3EnJ1\npFgdWTOyoSrA8/w1rck4c/ISXZSinVAggPxmLwVEAAln6Itj6giIZHKvA2fL2o8z\nCeK057Lu8X6u2CG8tRWSQzVoKIQw/PKK6CNXCAy8vo4EkXudRutnEYHEJlPkVgPn\n2qP06GI+I+9zKE37iqj0k1/wFaCVXHXIvn06YrmjQw6I0dDj/60Wvi500FuRVpn9\ntwIDAQAB\n-----END PUBLIC KEY-----", "key for verifying sketch binary signature") @@ -128,7 +128,7 @@ func main() { check(err, "CreateService") if *doLogin { - token, err := askCredentials(config.AuthURL) + token, err := deviceAuth(config.AuthURL) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) From 33a3cfe91a74915d8e55cc3aaa8672badfd2b1e5 Mon Sep 17 00:00:00 2001 From: Matteo Suppo Date: Tue, 26 May 2020 10:35:02 +0200 Subject: [PATCH 2/5] Remove clientid from being hardcoded --- auth/auth.go | 8 ++++---- install.go | 8 ++++---- main.go | 5 ++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/auth/auth.go b/auth/auth.go index cc748d39..87c60a7f 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -53,10 +53,10 @@ type DeviceCode struct { VerificationURIComplete string `json:"verification_uri_complete"` } -func StartDeviceAuth(authURL string) (data DeviceCode, err error) { +func StartDeviceAuth(authURL, clientID string) (data DeviceCode, err error) { url := authURL + "/oauth/device/code" - payload := strings.NewReader("client_id=ks1R298bA8IQnG4p6dPlbdEIXF6Kt1Lu&audience=https://api.arduino.cc") + payload := strings.NewReader("client_id=" + clientID + "&audience=https://api.arduino.cc") req, err := http.NewRequest("POST", url, payload) if err != nil { @@ -84,10 +84,10 @@ func StartDeviceAuth(authURL string) (data DeviceCode, err error) { return data, nil } -func CheckDeviceAuth(authURL, deviceCode string) (token string, err error) { +func CheckDeviceAuth(authURL, clientID, deviceCode string) (token string, err error) { url := authURL + "/oauth/token" - payload := strings.NewReader("grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code&device_code=" + deviceCode + "&client_id=ks1R298bA8IQnG4p6dPlbdEIXF6Kt1Lu") + payload := strings.NewReader("grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code&device_code=" + deviceCode + "&client_id=" + clientID) req, err := http.NewRequest("POST", url, payload) if err != nil { diff --git a/install.go b/install.go index e2fb16bb..64067ba9 100644 --- a/install.go +++ b/install.go @@ -65,7 +65,7 @@ func register(config Config, configFile, token string) { // Request token var err error if token == "" { - token, err = deviceAuth(config.AuthURL) + token, err = deviceAuth(config.AuthURL, config.AuthClientID) check(err, "deviceAuth") } @@ -146,8 +146,8 @@ func registerDeviceViaMQTT(config Config) { } // Implements Auth0 device authentication flow: https://auth0.com/docs/flows/guides/device-auth/call-api-device-auth -func deviceAuth(authURL string) (token string, err error) { - code, err := auth.StartDeviceAuth(authURL) +func deviceAuth(authURL, clientID string) (token string, err error) { + code, err := auth.StartDeviceAuth(authURL, clientID) if err != nil { return "", err } @@ -166,7 +166,7 @@ Loop: break Loop case <-ticker.C: var err error - token, err = auth.CheckDeviceAuth(authURL, code.DeviceCode) + token, err = auth.CheckDeviceAuth(authURL, clientID, code.DeviceCode) if err == nil { cancel() } diff --git a/main.go b/main.go index 485a636a..044ea440 100644 --- a/main.go +++ b/main.go @@ -59,6 +59,7 @@ type Config struct { HTTPSProxy string ALLProxy string AuthURL string + AuthClientID string APIURL string updateURL string appName string @@ -76,6 +77,7 @@ func (c Config) String() string { out += "https_proxy=" + c.HTTPSProxy + "\r\n" out += "all_proxy=" + c.ALLProxy + "\r\n" out += "authurl=" + c.AuthURL + "\r\n" + out += "auth_client_id=" + c.AuthClientID + "\r\n" out += "apiurl=" + c.APIURL + "\r\n" out += "cert_path=" + c.CertPath + "\r\n" out += "sketches_path=" + c.SketchesPath + "\r\n" @@ -110,6 +112,7 @@ func main() { flag.StringVar(&config.ALLProxy, "all_proxy", "", "URL of SOCKS proxy to use") flag.StringVar(&config.AuthURL, "authurl", "https://login.oniudra.cc", "Url of authentication server") flag.StringVar(&config.APIURL, "apiurl", "https://api-dev.arduino.cc", "Url of api server") + flag.StringVar(&config.AuthClientID, "authclientid", "", "Client ID for authentication server") flag.BoolVar(&config.CheckRoFs, "check_ro_fs", false, "Check for Read Only file system and remount if necessary") flag.BoolVar(&debugMqtt, "debug-mqtt", false, "Output all received/sent messages") flag.StringVar(&config.SignatureKey, "signature_key", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvc0yZr1yUSen7qmE3cxF\nIE12rCksDnqR+Hp7o0nGi9123eCSFcJ7CkIRC8F+8JMhgI3zNqn4cUEn47I3RKD1\nZChPUCMiJCvbLbloxfdJrUi7gcSgUXrlKQStOKF5Iz7xv1M4XOP3JtjXLGo3EnJ1\npFgdWTOyoSrA8/w1rck4c/ISXZSinVAggPxmLwVEAAln6Itj6giIZHKvA2fL2o8z\nCeK057Lu8X6u2CG8tRWSQzVoKIQw/PKK6CNXCAy8vo4EkXudRutnEYHEJlPkVgPn\n2qP06GI+I+9zKE37iqj0k1/wFaCVXHXIvn06YrmjQw6I0dDj/60Wvi500FuRVpn9\ntwIDAQAB\n-----END PUBLIC KEY-----", "key for verifying sketch binary signature") @@ -128,7 +131,7 @@ func main() { check(err, "CreateService") if *doLogin { - token, err := deviceAuth(config.AuthURL) + token, err := deviceAuth(config.AuthURL, config.AuthClientID) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) From e7e79108a1821f13cb5ea819c64ab55583ded55b Mon Sep 17 00:00:00 2001 From: Matteo Suppo Date: Tue, 26 May 2020 10:36:22 +0200 Subject: [PATCH 3/5] remove println --- install.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/install.go b/install.go index 64067ba9..abe5672f 100644 --- a/install.go +++ b/install.go @@ -69,8 +69,6 @@ func register(config Config, configFile, token string) { check(err, "deviceAuth") } - fmt.Println(token) - // Generate a Private Key and CSR csr := generateKeyAndCsr(config) From 0253e9bf7b5702d16c5ebfb7f40eed18d4bb7c6f Mon Sep 17 00:00:00 2001 From: Matteo Suppo Date: Wed, 27 May 2020 14:24:08 +0200 Subject: [PATCH 4/5] Embed the client ids instead of getting them from outside --- main.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 044ea440..f121d17d 100644 --- a/main.go +++ b/main.go @@ -110,9 +110,8 @@ func main() { flag.StringVar(&config.HTTPProxy, "http_proxy", "", "URL of HTTP proxy to use") flag.StringVar(&config.HTTPSProxy, "https_proxy", "", "URL of HTTPS proxy to use") flag.StringVar(&config.ALLProxy, "all_proxy", "", "URL of SOCKS proxy to use") - flag.StringVar(&config.AuthURL, "authurl", "https://login.oniudra.cc", "Url of authentication server") + flag.StringVar(&config.AuthURL, "authurl", "https://login.arduino.cc", "Url of authentication server") flag.StringVar(&config.APIURL, "apiurl", "https://api-dev.arduino.cc", "Url of api server") - flag.StringVar(&config.AuthClientID, "authclientid", "", "Client ID for authentication server") flag.BoolVar(&config.CheckRoFs, "check_ro_fs", false, "Check for Read Only file system and remount if necessary") flag.BoolVar(&debugMqtt, "debug-mqtt", false, "Output all received/sent messages") flag.StringVar(&config.SignatureKey, "signature_key", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvc0yZr1yUSen7qmE3cxF\nIE12rCksDnqR+Hp7o0nGi9123eCSFcJ7CkIRC8F+8JMhgI3zNqn4cUEn47I3RKD1\nZChPUCMiJCvbLbloxfdJrUi7gcSgUXrlKQStOKF5Iz7xv1M4XOP3JtjXLGo3EnJ1\npFgdWTOyoSrA8/w1rck4c/ISXZSinVAggPxmLwVEAAln6Itj6giIZHKvA2fL2o8z\nCeK057Lu8X6u2CG8tRWSQzVoKIQw/PKK6CNXCAy8vo4EkXudRutnEYHEJlPkVgPn\n2qP06GI+I+9zKE37iqj0k1/wFaCVXHXIvn06YrmjQw6I0dDj/60Wvi500FuRVpn9\ntwIDAQAB\n-----END PUBLIC KEY-----", "key for verifying sketch binary signature") @@ -120,6 +119,14 @@ func main() { flag.Parse() + if config.AuthURL == "https://login.oniudra.cc" { + config.AuthClientID = "ks1R298bA8IQnG4p6dPlbdEIXF6Kt1Lu" + } + + if config.AuthURL == "https://login.arduino.cc" { + config.AuthClientID = "QGdLCWFA4uQdbRE2NOFhUI8bnXWMZhCK" + } + if *configFile == "" { *configFile = defaultConfigFile } From 009941cd1ff06eb8ac53dc781c3da975550e91e9 Mon Sep 17 00:00:00 2001 From: Matteo Suppo Date: Thu, 28 May 2020 09:37:27 +0200 Subject: [PATCH 5/5] Restore default prod endpoints --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index f121d17d..eead6205 100644 --- a/main.go +++ b/main.go @@ -111,7 +111,7 @@ func main() { flag.StringVar(&config.HTTPSProxy, "https_proxy", "", "URL of HTTPS proxy to use") flag.StringVar(&config.ALLProxy, "all_proxy", "", "URL of SOCKS proxy to use") flag.StringVar(&config.AuthURL, "authurl", "https://login.arduino.cc", "Url of authentication server") - flag.StringVar(&config.APIURL, "apiurl", "https://api-dev.arduino.cc", "Url of api server") + flag.StringVar(&config.APIURL, "apiurl", "https://api2.arduino.cc", "Url of api server") flag.BoolVar(&config.CheckRoFs, "check_ro_fs", false, "Check for Read Only file system and remount if necessary") flag.BoolVar(&debugMqtt, "debug-mqtt", false, "Output all received/sent messages") flag.StringVar(&config.SignatureKey, "signature_key", "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvc0yZr1yUSen7qmE3cxF\nIE12rCksDnqR+Hp7o0nGi9123eCSFcJ7CkIRC8F+8JMhgI3zNqn4cUEn47I3RKD1\nZChPUCMiJCvbLbloxfdJrUi7gcSgUXrlKQStOKF5Iz7xv1M4XOP3JtjXLGo3EnJ1\npFgdWTOyoSrA8/w1rck4c/ISXZSinVAggPxmLwVEAAln6Itj6giIZHKvA2fL2o8z\nCeK057Lu8X6u2CG8tRWSQzVoKIQw/PKK6CNXCAy8vo4EkXudRutnEYHEJlPkVgPn\n2qP06GI+I+9zKE37iqj0k1/wFaCVXHXIvn06YrmjQw6I0dDj/60Wvi500FuRVpn9\ntwIDAQAB\n-----END PUBLIC KEY-----", "key for verifying sketch binary signature")