La API de Box siempre devuelve un parámetro grant_type no válido al obtener el token de acceso

Estoy escribiendo mi propio Box SDK para WP8 para aprovechar al máximo las tareas. Estoy teniendo problemas para obtener un token de acceso. Siempre recibo esto como una devolución:

{"error":"invalid_request","error_description":"Invalid grant_type parameter or parameter missing"}

El código (todo en C #) que estoy usando es:

    internal const String TokenURL = "https://api.box.com/oauth2/token";

CloudHttpAsync.DownloadResponceStreamAsync
(
    CloudHttpAsync.PostAsync
    (
        TokenURL,
        new MemoryStream
        (
            UTF8Encoding.UTF8.GetBytes
            (
                HttpUtility.UrlEncode
                (
                    String.Format
                    (
                        "grant_type=authorization_code&code={0}&client_id={1}&client_secret={2}&redirect_uri={3}",
                        Code,
                        ClientID,
                        ClientSecret,
                        RedirectURI
                    )
                )
            )
        ),
        null,
        null
    ),
    null
).ContinueWith((AsyncStream) =>
    {
        try
        {
            if (AsyncStream.Exception != null)
            {
                TaskSource.TrySetException(AsyncStream.Exception.InnerExceptions);
                return;
            }

            String Result = "";
            using (StreamReader Reader = new StreamReader(AsyncStream.Result))
            {
                Result = Reader.ReadToEnd();
            }

            BoxAuthToken Token = JsonConvert.DeserializeObject<BoxAuthToken>(Result);
            TaskSource.TrySetResult(Token);
        }
        catch (Exception e)
        {
            TaskSource.TrySetException(e);
        }
    });

y

    public static Task<HttpWebResponse> PostAsync(String URL, Stream UploadData, IRequestSigner Signer, IProgress<NetworkProgress> Progress)
    {
        TaskCompletionSource<HttpWebResponse> TaskSource = new TaskCompletionSource<HttpWebResponse>();

        HttpWebRequest Request = WebRequest.CreateHttp(URL);
        Request.Method = "POST";

        if (Signer != null)
        {
            Signer.Sign(Request).ContinueWith((o) =>
            {
                if (o.Exception != null)
                {
                    TaskSource.TrySetException(o.Exception.InnerExceptions);
                    return;
                }

                UploadDataAsync(Request, UploadData, Progress).ContinueWith((AsyncRequest) =>
                {
                    if (AsyncRequest.Exception != null)
                    {
                        TaskSource.TrySetException(AsyncRequest.Exception.InnerExceptions);
                        return;
                    }

                    GetResponceAsync(Request).ContinueWith((AsyncResponce) =>
                    {
                        if (AsyncResponce.Exception != null)
                        {
                            TaskSource.TrySetException(AsyncResponce.Exception.InnerExceptions);
                            return;
                        }

                        TaskSource.TrySetResult(AsyncResponce.Result);
                    });
                });
            });
        }
        else
        {
            UploadDataAsync(Request, UploadData, Progress).ContinueWith((AsyncRequest) =>
            {
                if (AsyncRequest.Exception != null)
                {
                    TaskSource.TrySetException(AsyncRequest.Exception.InnerExceptions);
                    return;
                }

                GetResponceAsync(Request).ContinueWith((AsyncResponce) =>
                {
                    if (AsyncResponce.Exception != null)
                    {
                        TaskSource.TrySetException(AsyncResponce.Exception.InnerExceptions);
                        return;
                    }

                    TaskSource.TrySetResult(AsyncResponce.Result);
                });
            });
        }

        return TaskSource.Task;
    }

    internal static Task<HttpWebRequest> UploadDataAsync(HttpWebRequest Request, Stream Data, IProgress<NetworkProgress> Progress)
    {
        TaskCompletionSource<HttpWebRequest> TaskSource = new TaskCompletionSource<HttpWebRequest>();

        if (Data.Length != 0)
        {
            Request.ContentLength = Data.Length;
            Request.AllowWriteStreamBuffering = false;

            Request.BeginGetRequestStream(new AsyncCallback((IAR) =>
                {
                    try
                    {
                        using (Stream UploadStream = Request.EndGetRequestStream(IAR))
                        {
                            Int64 Upload = 0;
                            Int64 TotalUploaded = 0;
                            Int64 Total = Data.Length;
                            Byte[] Buffer = new Byte[4096];

                            while (TotalUploaded < Total)
                            {
                                Upload = Data.Read(Buffer, 0, Buffer.Length);
                                TotalUploaded += Upload;
                                UploadStream.Write(Buffer, 0, (Int32)Upload);

                                if (Progress != null)
                                {
                                    Progress.Report(new NetworkProgress()
                                    {
                                        Operation = NetworkOperation.Uploading,
                                        TotalBytes = Total,
                                        BytesProcessed = TotalUploaded
                                    });
                                }
                            }
                        }

                        TaskSource.TrySetResult(Request);
                    }
                    catch (Exception e)
                    {
                        TaskSource.TrySetException(e);
                    }
                }),
                null);
        }
        else
        {
            TaskSource.TrySetResult(Request);
        }

        return TaskSource.Task;
    }

    internal static Task<HttpWebResponse> GetResponceAsync(HttpWebRequest Request)
    {
        TaskCompletionSource<HttpWebResponse> TaskSource = new TaskCompletionSource<HttpWebResponse>();

        Request.BeginGetResponse(new AsyncCallback((IAR) =>
            {
                try
                {
                    HttpWebResponse Responce = (HttpWebResponse)Request.EndGetResponse(IAR);
                    TaskSource.TrySetResult(Responce);
                }
                catch (Exception e)
                {
                    if (e is WebException && (e as WebException).Response.ContentLength > 0)
                    {
                        TaskSource.TrySetResult((HttpWebResponse)(e as WebException).Response);
                    }
                    else
                    {
                        TaskSource.TrySetException(e);
                    }
                }
            }),
            null);

        return TaskSource.Task;
    }

    public static Task<StreamAndLength> GetResponceStreamAsync(Task<HttpWebResponse> Task)
    {
        TaskCompletionSource<StreamAndLength> TaskSource = new TaskCompletionSource<StreamAndLength>();

        Task.ContinueWith((AsyncHWR) =>
            {
                if (AsyncHWR.Exception != null)
                {
                    TaskSource.TrySetException(AsyncHWR.Exception.InnerExceptions);
                    return;
                }

                HttpWebResponse Responce = AsyncHWR.Result;
                TaskSource.TrySetResult( new StreamAndLength() { Stream = Responce.GetResponseStream(), Length = Responce.ContentLength });
            });

        return TaskSource.Task;
    }

    public static Task<MemoryStream> DownloadResponceStreamAsync(Task<HttpWebResponse> Task, IProgress<NetworkProgress> Progress)
    {
        TaskCompletionSource<MemoryStream> TaskSource = new TaskCompletionSource<MemoryStream>();

        GetResponceStreamAsync(Task).ContinueWith((AsyncStream) =>
            {
                if (AsyncStream.Exception != null)
                {
                    TaskSource.TrySetException(AsyncStream.Exception.InnerExceptions);
                    return;
                }

                MemoryStream MemStream = new MemoryStream();
                MemStream.SetLength(AsyncStream.Result.Length);

                Int64 CurrentRead = 0;
                Int64 TotalRead = 0;
                Int64 Total = AsyncStream.Result.Length;
                Byte[] Buffer = new Byte[4096];

                using (Stream DownloadStream = AsyncStream.Result.Stream)
                while (TotalRead < Total)
                {
                    CurrentRead = DownloadStream.Read(Buffer, 0, Buffer.Length);
                    MemStream.Write(Buffer, 0, (Int32)CurrentRead);
                    TotalRead += CurrentRead;

                    if (Progress != null)
                    {
                        Progress.Report(new NetworkProgress()
                        {
                            Operation = NetworkOperation.Downloading,
                            TotalBytes = Total,
                            BytesProcessed = TotalRead
                        });
                    }
                }

                MemStream.Position = 0;
                TaskSource.TrySetResult(MemStream);
            });

        return TaskSource.Task;
    }

    internal class StreamAndLength
    {
        public Stream Stream { get; set; }
        public Int64 Length { get; set; }
    }

Lo siento, hay un montón de código, me gusta escribir genéricamente :)

Edit: Raw Responces (ClientID & Client Secret eliminado)

Cuando la URL codifica cada valor:

POST https://api.box.com/oauth2/token HTTP/1.1
Accept: */*
Content-Length: 196
Accept-Encoding: identity
User-Agent: NativeHost
Host: api.box.com
Connection: Keep-Alive
Cache-Control: no-cache

grant_type=authorization_code&code=JknaLbfT6lAXmey3FLYrp9eg1jMbpFuQ&client_id=[subbed]&client_secret=[subbed]&redirect_uri=https%3a%2f%2fCloudBoxWP8

Regreso:

HTTP/1.1 400 Bad Request
Server: nginx
Date: Fri, 01 Mar 2013 07:35:22 GMT
Content-Type: application/json
Connection: keep-alive
Set-Cookie: box_visitor_id=51305a3a187f34.52738262; expires=Sat, 01-Mar-2014 07:35:22 GMT; path=/; domain=.box.com
Set-Cookie: country_code=US; expires=Tue, 30-Apr-2013 07:35:22 GMT; path=/
Cache-Control: no-store
Content-Length: 99

{"error":"invalid_request","error_description":"Invalid grant_type parameter or parameter missing"}

Cuando la URL codifica toda la cadena:

POST https://api.box.com/oauth2/token HTTP/1.1
Accept: */*
Content-Length: 214
Accept-Encoding: identity
User-Agent: NativeHost
Host: api.box.com
Connection: Keep-Alive
Cache-Control: no-cache

grant_type%3dauthorization_code%26code%3d3ikruv5elfdw3fOP55aMDSX7ybLqBFlA%26client_id%3d[subbed]%26client_secret%3d[subbed]%26redirect_uri%3dhttps%3a%2f%2fCloudBoxWP8

Regreso

HTTP/1.1 400 Bad Request
Server: nginx
Date: Fri, 01 Mar 2013 07:46:03 GMT
Content-Type: application/json
Connection: keep-alive
Set-Cookie: box_visitor_id=51305cbb339de4.03221876; expires=Sat, 01-Mar-2014 07:46:03 GMT; path=/; domain=.box.com
Set-Cookie: country_code=US; expires=Tue, 30-Apr-2013 07:46:03 GMT; path=/
Cache-Control: no-store
Content-Length: 99

{"error":"invalid_request","error_description":"Invalid grant_type parameter or parameter missing"}

Sin codificación de URL:

POST https://api.box.com/oauth2/token HTTP/1.1
Accept: */*
Content-Length: 190
Accept-Encoding: identity
User-Agent: NativeHost
Host: api.box.com
Connection: Keep-Alive
Cache-Control: no-cache

grant_type=authorization_code&code=2wgIzfqhvIgRtVIp2ZvqZ9X8R5u0QNaf&client_id=[subbed]&client_secret=[subbed]&redirect_uri=https://CloudBoxWP8

Regreso:

HTTP/1.1 400 Bad Request
Server: nginx
Date: Fri, 01 Mar 2013 07:50:31 GMT
Content-Type: application/json
Connection: keep-alive
Set-Cookie: box_visitor_id=51305dc751d7f5.67064854; expires=Sat, 01-Mar-2014 07:50:31 GMT; path=/; domain=.box.com
Set-Cookie: country_code=US; expires=Tue, 30-Apr-2013 07:50:31 GMT; path=/
Cache-Control: no-store
Content-Length: 99

{"error":"invalid_request","error_description":"Invalid grant_type parameter or parameter missing"}

Respuestas a la pregunta(4)

Su respuesta a la pregunta