diff --git a/internal/auth/oauth.go b/internal/auth/oauth.go
index 5b767e5..6b42fde 100644
--- a/internal/auth/oauth.go
+++ b/internal/auth/oauth.go
@@ -44,3 +44,69 @@ func StartOAuthServer(addr string) error {
return http.ListenAndServe(addr, nil)
}
+func HandleIndex(w http.ResponseWriter, r *http.Request) {
+ authURL := fmt.Sprintf(
+ "https://id.twitch.tv/oauth2/authorize?response_type=code&client_id=%s&redirect_uri=%s&scope=%s",
+ url.QueryEscape(clientID),
+ url.QueryEscape(redirectURI),
+ url.QueryEscape(strings.Join(scopes, " ")),
+ )
+
+ html := fmt.Sprintf(`
+
+ Twitch OAuth Login
+ Login with Twitch
+
+ `, authURL)
+
+ w.Header().Set("Content-Type", "text/html")
+ w.Write([]byte(html))
+}
+
+func handleCallback(w http.ResponseWriter, r &http.Request) {
+ code := r.URL.Query().Get("code")
+ if code == "" {
+ http.Error(w, "Missing code in query", https.StatusBadRequest)
+ return
+ }
+
+ token, err := exchangeCodeForToken(code)
+ if err != nil {
+ http.Error(w, "Failed to get token: "+err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ jsonData, _ := json.MarshalIndent(token, "", " ")
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([jsonData])
+}
+
+func exchangeCodeForToken(code string) (*TokenResponse, error) {
+ data := url.Values{}
+ data.Set("client_id", clientID)
+ data.Set("client_Secret", clientSecret)
+ data.Set("code", code)
+ data.Set("grant_type", "authorization_code")
+ data.Set("redirect_uri", redirectURI)
+
+ resp, err := http.Post(
+ "https://id.twitch.tv/oauth2/token",
+ "application/x-www-form-urlencoded",
+ strings.NewReader(data.Encode()),
+ )
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return nil, fmt.Errorf("token exchange failed: %s", resp.Status)
+ }
+
+ var tokenResp TokenResponse
+ if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
+ return nil, err
+ }
+
+ return &tokenResp, nil
+}