Andrew
Andrew

Reputation: 1

QuickBooks Online Oauth2 token request yields "Bad Request"

I have been battling with Oauth2 while trying to link up a .NET application written in VB with qbo (quickbooks online). I can get my code but I get a "(400) Bad Request" response when trying to exchange the code for my access and refresh token. Here is my code for requesting the access token:

Function GetTokens(strCode As String, strURL As String, strClientID As String, strClientSecret As String, strRedirectURI As String) As Boolean
    Dim strBasic, strPostData As String
    Dim strResponse As String
    strBasic = strClientID & ":" & strClientSecret
    Dim byt As Byte() = System.Text.Encoding.UTF8.GetBytes(strBasic)
    strBasic = Convert.ToBase64String(byt)
    strPostData = "grant_type=authorization_code"
    strPostData += "&code=" & strCode
    strPostData += "&redirect_uri=" & strRedirectURI
    strResponse = ReadURLwithAuthentication("POST", strURL, strPostData, strBasic)
    Response.Write(strResponse)
    Return True
End Function

    Function ReadURLwithAuthentication(strMethod As String, strURL As String, strPostData As String, strToken As String) As String
        Dim wrX As WebRequest = WebRequest.Create(strURL)
        'request method
        wrX.Method = strMethod
        'protocal version
        CType(wrX, HttpWebRequest).ProtocolVersion = HttpVersion.Version11
        'accept
        CType(wrX, HttpWebRequest).Accept = "application/json"
        'authorization
        wrX.Headers.Add("Authorization", "Basic " + strToken)
        'content type
        wrX.ContentType = "application/x-www-form-urlencoded"
        'host
        CType(wrX, HttpWebRequest).Host = "oauth.platform.intuit.com"
        'contentbody only supplied for POST
        Select Case strMethod
            Case "POST", "DELETE"
                'convert post data to a byte array.
                Dim byteArray As Byte() = Encoding.UTF8.GetBytes(strPostData)
                'content length
                wrX.ContentLength = byteArray.Length
                'get the stream that holds the request
                Dim smX As Stream = wrX.GetRequestStream()
                'write the data to the stream object
                smX.Write(byteArray, 0, byteArray.Length)
                'close the stream
                smX.Close()
        End Select
        'get the response object
        Dim strResponse As String
        Try
            Dim respX As WebResponse = wrX.GetResponse()
            'get response stream
            Dim smY As Stream = respX.GetResponseStream
            'examine the results
            Dim encode As Encoding = System.Text.Encoding.GetEncoding("utf-8")
            Dim sw As New StreamReader(smY, encode)
            strResponse = sw.ReadToEnd
            'close stream
            smY.Close()
            'close the response object
            respX.Close()
            'close stream reader
            sw.Close()
        Catch ex As Exception
            strResponse = ex.Message.ToString
        End Try
        Return strResponse
    End Function

Upvotes: 0

Views: 644

Answers (2)

Andrew
Andrew

Reputation: 1

It turns out the main problem was that I was submitting two different redirect_uri's. Since Jan Halasa kindly provided the link to the OAuth2 RFC, I learned that the redirect_uri has to be exactly the same for the code request and the follow-up token request. The httputility.encode() was not necessary since my redirect_uri did not include any ampersands or other special characters.

Upvotes: 0

Ján Halaša
Ján Halaša

Reputation: 8431

Bad request almost certainly means you are sending invalid data. So the easiest way to solve the problem would be to get a log of the request and response and analyze it. The request should look like the one from the OAuth2 RFC.

But looking at your code, I see one thing that could cause the problem - you are sending a request with application/x-www-form-urlencoded content type, but you don't encode its data. If the values (auth code or redirectUri) contain characters such as the ampersand &, it will unintentionally introduce new request parameters (&something=) and make the original values invalid. So your code should be something like this:

strPostData = "grant_type=authorization_code"
strPostData += "&code=" & HttpUtility.UrlEncode(strCode)
strPostData += "&redirect_uri=" & HttpUtility.UrlEncode(strRedirectURI)

And in the ReadURLwithAuthentication method, the Case "POST", "DELETE" should probably be for POST and PUT methods, because a DELETE request cannot have a body.

Upvotes: 1

Related Questions