package oauth import ( "encoding/json" "fmt" "log" "net/http" "net/url" "os" "strings" ) var ( clientID = os.Getenv("TWITCH_CLIENT_ID") clientSecret = os.Getenv("TWITCH_CLIENT_SECRET") redirectURI = "http://localhost:8080/callback" scopes = []string{ "chat:read", "chat:edit", "channel:read:subscriptions", "user:read:email", } ) type TokenResponse struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` ExpiresIn int `json:"expires_in"` Scope []string `json:"scope"` TokenType string `json:"token_type"` } // start http server for oauth login & callback func StartOAuthServer(addr string) error { if clientID == "" || clientSecret == "" { return fmt.Errorf("TWITCH_CLIENT_ID and TWITCH_CLIENT_SECRET must be set") } http.HandleFunc("/", handleIndex) http.HandleFunc("/callback", handleCallback) log.Printf("OAuth server listening on %s", addr) 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 }