Search code examples
c#bad-request

Getting Error 400: Bad request at authenticate via Twitch.tv api


I am new here and I hope someone can help me. I try to connect to twitch.tv I am trying to get an oauth2 authentication on twitch.tv with a small C# program. I am using the twitch.tv authentication request. Here is my C# code:

    var loginURL = "https://api.twitch.tv/kraken/oauth2/authorize?
                               response_type=code&"+
                               client_id="+ clientID+"
                               "&redirect_uri=http://localhost&"+
                               "state=TWStreamingStateAuthenticated";                    
    this.richTextBox1.Text = loginURL;
    string code = get_DownLoadString(loginURL);
    this.richTextBox1.Text = code;

This is the part, which does not work. It gives me the Error 400: Bad Request.

    WebRequest request = WebRequest.Create("https://api.twitch.tv/kraken/oauth2/token");
    request.Method = "POST";
    string postData = "client_id=" + clientID +
                      "&client_secret=" + clientSecret +
                      "&grant_type=authorization_code" +
                      "&redirect_uri=http://localhost" +
                      "&code=" + code +
                      "&state=TWStreamingStateAuthenticated";

    ASCIIEncoding encoding = new ASCIIEncoding();
    postData = HttpUtility.UrlEncode(postData);            
    byte[] byteArray = encoding.GetBytes(postData);
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = byteArray.Length;
    Stream datatream = request.GetRequestStream();
    datatream.Write(byteArray, 0, byteArray.Length);
    datatream.Close();
    WebResponse respone = request.GetResponse();
    MessageBox.Show(((HttpWebResponse)respone).StatusDescription);

I hope someone can help me. And here is the Get_DownloadString(string URL) Method.

private static string get_DownLoadString(string URL)
{
    try
    {
        string temp = (new WebClient().DownloadString(URL));
        return temp;
    }
    catch (WebException)
    {
        return null;
    }
}

Solution

  • This code doesn't look right to me:

        string postData = "client_id=" + clientID +
                          "&client_secret=" + clientSecret +
                          "&grant_type=authorization_code" +
                          "&redirect_uri=http://localhost" +
                          "&code=" + code +
                          "&state=TWStreamingStateAuthenticated";
    
        ASCIIEncoding encoding = new ASCIIEncoding();
        postData = HttpUtility.UrlEncode(postData);
        byte[] byteArray = encoding.GetBytes(postData);
        // ...
    

    You are URL-encoding the entire post-data string. This has the effect of converting the & and = signs in the post data to %26 and %3d respectively. When the remote server receives this data, it will scan through it looking for the & and = signs in order to separate out the parameter names and values. Of course, it won't find any, so it will assume you have one big parameter name with no value. The server is probably expecting values for each of the six parameters you are attempting to send, but seeing values for none of them, and this may be why you are getting a 400 Bad Request error.

    Instead of URL-encoding the whole string, URL-encode parameter values that may contain characters other than letters and numbers. I would try the following instead:

        string postData = "client_id=" + HttpUtility.UrlEncode(clientID) +
                          "&client_secret=" + HttpUtility.UrlEncode(clientSecret) +
                          "&grant_type=authorization_code" +
                          "&redirect_uri=" + HttpUtility.UrlEncode("http://localhost") +
                          "&code=" + HttpUtility.UrlEncode(code) +
                          "&state=TWStreamingStateAuthenticated";
    
        ASCIIEncoding encoding = new ASCIIEncoding();
        byte[] byteArray = encoding.GetBytes(postData);
        // ...
    

    This way, the remote server will still see the & and = characters, and so will be able to pull out the parameter names and values. Because we've URL-encoded the client ID, client secret, URL and code, any characters they contain that may have meaning in a URL will not have that meaning and will be received by the remote server as intended.

    Also, if you are still getting a 400 Bad Request error response, try reading the contents of the response stream, obtained by calling GetResponseStream() on the response. Often that will contain a message that will help you figure out what's gone wrong.


    Having had a closer look at your code, it seems you have a misunderstanding about how OAuth authentication works. Your getDownload_String method will not get the access code you want, it will only get the HTML text of a Twitch login page.

    This is how OAuth authentication works:

    1. Your app sends the user to a login URL, to allow the user to log in to Twitch.
    2. In the web browser, the user then enters their login credentials and submits the page to Twitch.
    3. The Twitch API then responds by redirecting the user's web browser to the redirect URL, with a code appended. Your web app then reads this code out of the URL.

    If your code is in a web app it will be able to respond to the URL redirected to in step 3. Alternatively, you may be able to use a WebBrowser control (Windows Forms, WPF) to handle the Twitch login, and handle a Navigating event. If the URL being navigated to begins with the redirect URL, grab the code out of the URL, cancel the navigation and hide the login web-browser control.

    The presence of what appears to be a RichTextBox control, along with your comment about your code being a 'small C# application', makes me think that your code is a Windows Forms or WPF application. If this is the case, then you will need to either:

    • use a WebBrowser control as I described above,
    • replace your WinForms/WPF app with a web app, or
    • get in contact with Twitch to request the use of the password flow (which appears not to require a redirect), and use that instead.