Skip to content

Commit f9e7770

Browse files
committed
fix shit up
1 parent 3e99a26 commit f9e7770

File tree

1 file changed

+67
-55
lines changed

1 file changed

+67
-55
lines changed

utils/requests/request_with_auth.go

Lines changed: 67 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package requests
22

33
import (
4+
"context"
45
"encoding/json"
56
"errors"
67
"fmt"
78
"io"
89
"net/http"
910
"net/url"
1011
"os"
11-
"strings"
1212

1313
"github.com/cli/browser"
1414

@@ -34,13 +34,68 @@ func NewRequestWithAuth(method, url string, body io.Reader) (*http.Request, erro
3434
if cfg.Env == "development" {
3535
token = "dev-token"
3636
} else {
37+
// Production OAuth2 Flow
3738
clientID := os.Getenv("DISCORD_CLIENT_ID")
3839
if clientID == "" {
3940
return nil, errors.New("DISCORD_CLIENT_ID is unset")
4041
}
42+
clientSecret := os.Getenv("DISCORD_CLIENT_SECRET")
43+
if clientSecret == "" {
44+
return nil, errors.New("DISCORD_CLIENT_SECRET is unset")
45+
}
4146
// TODO: check that this port isn't being used first
4247
const redirectURI = "http://localhost:8888"
4348
const scope = "identify"
49+
50+
tokenChan := make(chan string)
51+
errChan := make(chan error)
52+
mux := http.NewServeMux()
53+
server := &http.Server{Addr: ":8888", Handler: mux}
54+
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
55+
code := r.URL.Query().Get("code")
56+
if code == "" {
57+
fmt.Fprintln(w, "No code found")
58+
return
59+
}
60+
fmt.Fprintf(w, "Got code! You can close this window.")
61+
62+
data := url.Values{}
63+
data.set("client_id", clientID)
64+
data.set("client_secret", clientSecret)
65+
data.set("grant_type", "authorization_code")
66+
data.set("code", code)
67+
data.set("redirect_uri", redirectURI)
68+
69+
resp, err := http.PostForm("https://discord.com/api/oauth2/token", data)
70+
if err != nil {
71+
errChan <- fmt.Errorf("failed to exchange token: %w", err)
72+
return
73+
}
74+
defer resp.Body.Close()
75+
76+
respBody, _ := io.ReadAll(resp.Body)
77+
78+
if resp.StatusCode != http.StatusOK {
79+
errChan <- fmt.Errorf("Discord API error: %s", string(respBody))
80+
}
81+
82+
var tokenResp TokenResponse
83+
if err := json.Unmarshal(respBody, &tokenResp); err != nil {
84+
errChan <- fmt.Errorf("JSON parse error: %w", err)
85+
}
86+
87+
tokenChan <- tokenResp.AccessToken
88+
})
89+
90+
// So server doesn't block
91+
go func() {
92+
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
93+
errChan <- err
94+
}
95+
}()
96+
97+
// Human needs to open browser to get the token
98+
// A web client can do this more gracefully, but we have to do this since we're on a CLI
4499
params := url.Values{}
45100
params.Add("client_id", clientID)
46101
params.Add("redirect_uri", redirectURI)
@@ -49,62 +104,19 @@ func NewRequestWithAuth(method, url string, body io.Reader) (*http.Request, erro
49104

50105
authURL := "https://discord.com/oauth2/authorize" + params.Encode()
51106
fmt.Println("Opening browser to:", authURL)
52-
browser.OpenURL(authURL)
53107
// TODO: "Press enter to open the following link in your browser"
54-
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
55-
code := r.URL.Query().Get("code")
56-
if code != "" {
57-
fmt.Fprintf(w, "Got code! You can close this window.\n\nCode: %s", code)
58-
fmt.Printf("Success! Auth code: %s\n", code)
59-
60-
fmt.Println("Exchanging code for access token...")
61-
data := url.Values{}
62-
data.set("client_id", clientID)
63-
clientSecret := os.Getenv("DISCORD_CLIENT_SECRET")
64-
if clientSecret == "" {
65-
fmt.Fprintf(os.Stderr, "DISCORD_CLIENT_SECRET is unset")
66-
return
67-
}
68-
data.set("client_secret", clientSecret)
69-
data.set("grant_type", "authorization_code")
70-
data.set("code", code)
71-
data.set("redirectURI", redirectURI)
72-
73-
req, _ := http.NewRequest("POST", "https://discord.com/api/oauth2/token",
74-
strings.NewReader(data.Encode()))
75-
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
76-
77-
client := &http.Client{}
78-
resp, err := client.Do(req)
79-
if err != nil {
80-
fmt.Printf("Error exchanging token: %v\n", err)
81-
return
82-
}
83-
84-
defer resp.Body.Close()
85-
body, _ := io.ReadAll(resp.Body)
86-
87-
var tokenResp TokenResponse
88-
if err := json.Unmarshal(body, &tokenResp); err != nil {
89-
fmt.Printf("Error parsing JSON: %v\nResponse Body: %s\n", err, string(body))
90-
return
91-
}
92-
93-
if tokenResp.AccessToken != "" {
94-
token = tokenResp.AccessToken
95-
} else {
96-
fmt.Fprintf(os.Stderr, "Error: Failed to get token. Discord said:\n%s\n", string(body))
97-
}
98-
99-
go func() {
100-
return
101-
}()
102-
} else {
103-
fmt.Fprintln(os.Stderr, "Error: no code in URL")
104-
}
105-
})
108+
browser.OpenURL(authURL)
109+
110+
// Block until we get a token or err
111+
select {
112+
case t := <-tokenChan:
113+
token = t
114+
case e := <-errChan:
115+
server.Shutdown(context.Background())
116+
return nil, e
117+
}
106118

107-
http.ListenAndServe(":8888", nil)
119+
server.Shutdown(context.Background())
108120
}
109121

110122
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))

0 commit comments

Comments
 (0)