I am attempting to perform Device Authorization Flow on a CLI in Go. I have followed the steps in https://auth0.com/blog/securing-a-python-cli-application-with-auth0/ to set up my application in Auth0. After successfully requesting a device code, I attempt to get a request token.
// Gets a request token.func (loginJob *LoginJob) GetRequestToken(deviceCodeData loginResponse.LResponse) error { //Setup an http request to retreive a request token. url := loginJob.Domain +"/oauth/token" method := "POST" payload := strings.NewReader("grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code&device_code=" + deviceCodeData.DeviceCode +"&client_id=" + loginJob.ClientID) client := &http.Client{} req, reqErr := http.NewRequest(method, url, payload) if reqErr != nil { fmt.Println(reqErr) return reqErr } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") authenticate := false for !authenticate { authenticate = PollRequestTokenStatus(req, client) time.Sleep(time.Duration(deviceCodeData.Interval) * time.Second) } return nil}func PollRequestTokenStatus(req *http.Request, client *http.Client) bool { res, resErr := client.Do(req) if resErr != nil { log.Panic(resErr) return true } defer res.Body.Close() body, ReadAllErr := io.ReadAll(res.Body) if ReadAllErr != nil { fmt.Println(ReadAllErr) return true } fmt.Println("res.Body: ") fmt.Println(string(body)) if res.StatusCode == 200 { fmt.Println("Authenticated!") fmt.Println("- Id Token: ") return true } else if res.StatusCode == 400 { fmt.Println("res.StatusCode: ") fmt.Print(res.StatusCode) return false } else { fmt.Println("res.StatusCode: ") fmt.Print(res.StatusCode) } return false}
The idea is that I poll Auth0 for a request token at specific intervals. The first time I poll, I get a 403 Forbidden:
{"error":"authorization_pending","error_description":"User has yet to authorize device code."}
However, on subsequent polls, I get 400 Bad Request:
<html><head><title>400 Bad Request</title></head><body><center><h1>400 Bad Request</h1></center><hr><center>cloudflare</center></body></html>
I am unsure why this is happening. I have tried manually polling Auth0 with Postman, and I have always managed to avoid error 400 using Postman.How do I fix this problem?
Update: It turns out for some reason Go will reset all the request headers whenever it calls *http.Client.Do(), so I tried moving the request construction inside the for loop. Now my code looks like this:
// Gets a request token.func (loginJob *LoginJob) GetRequestToken(deviceCodeData loginResponse.LResponse) error { // Setup an http request to retreive a request token. url := loginJob.Domain +"/oauth/token" method := "POST" payload := strings.NewReader("grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code&device_code=" + deviceCodeData.DeviceCode +"&client_id=" + loginJob.ClientID) authenticate := false var pollErr error for !authenticate { authenticate, pollErr = loginJob.PollRequestTokenStatus(url, method, payload, deviceCodeData) if pollErr != nil { log.Panic(pollErr) } time.Sleep(time.Duration(deviceCodeData.Interval) * time.Second) } return nil}func (loginJob *LoginJob) PollRequestTokenStatus(url string, method string, payload *strings.Reader, deviceCodeData loginResponse.LResponse) (bool, error) {// Setup an http request to retreive a request token. client := &http.Client{ Timeout: time.Second * 10, } req, reqErr := http.NewRequest(method, url, payload) if reqErr != nil { fmt.Println(reqErr) return false, reqErr } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") res, resErr := client.Do(req) if resErr != nil { fmt.Println(resErr) return false, resErr } defer res.Body.Close() fmt.Println("res.StatusCode:") fmt.Println(res.StatusCode) if res.StatusCode == 200 { fmt.Println("Authenticated!") fmt.Println("- Id Token: ") return true, nil } else if res.StatusCode == 400 { return true, nil } return false, nil}
I have a different issue now. Instead of returning error 400 on subsequent polls, I am getting 401 instead:
{"error":"access_denied","error_description":"Unauthorized"}