Search code examples
c#file-uploaddotnet-httpclienthttp-status-code-401

HttpClient Unauthorized with Bearer Token


Due to the fact that HttpWebRequest is obsolete I'm in the process of upgrading to the equivalent in .NET 6.

I'm not understanding why I'm getting a 401 unauthorized. I can get the bearer token successfully but I just can't seem to find the right combination to successfully upload the file.

private static async Task UploadCsvFileAsync(string authToken)
{
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri("https://subdomain.domain.com/");
        client.DefaultRequestHeaders
            .Accept
            .Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
        using (var request = new HttpRequestMessage())
        {
            request.RequestUri = new Uri(uploadUrl);
            request.Method = HttpMethod.Post;
            request.Headers.Add("api-version", "1");
            request.Headers.Add("Authorization", $"Bearer {authToken}");

            string header = $"{Environment.NewLine}--{formBoundary}{Environment.NewLine}" +
                $"Content-Disposition: form-data; name=\"csvfile\"; filename=\"{csvFileName}\"{Environment.NewLine}" +
                $"Content-Type: text/csv{Environment.NewLine}{Environment.NewLine}";
            byte[] headerBytes = Encoding.UTF8.GetBytes(header);
            var headerContent = new StreamContent(new MemoryStream(headerBytes));
            headerContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
            var content = new MultipartFormDataContent();
            content.Add(headerContent, "metadata", csvFileName);

            var response = await client.PostAsync(uploadUrl, content);
            // the response code is 401 - Unauthorized
        }
    }
}

I have working .NET Framework 4.x code working. Since we're migrating our code to .NET 6 it has to be re-written.

Obviously I'm doing something wrong - I just can't seem to figure out what I'm missing.

Any help is greatly appreciated. Thank you in advance.

Edit: 03/29/2023 - including correct code as provided by answer below.

private static async Task UploadCsvFileAsyncV2(string authToken)
{
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri(uploadUrl);
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
        client.DefaultRequestHeaders
            .Accept
            .Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
        using (var request = new HttpRequestMessage(HttpMethod.Post, "uploads"))
        {
            request.Headers.Add("api-version", "1");
            string header = $"{Environment.NewLine}--{formBoundary}{Environment.NewLine}" +
                $"Content-Disposition: form-data; name=\"csvfile\"; filename=\"{csvFileName}\"{Environment.NewLine}" +
                $"Content-Type: text/csv{Environment.NewLine}{Environment.NewLine}";
            byte[] headerBytes = Encoding.UTF8.GetBytes(header);
            var headerContent = new StreamContent(new MemoryStream(headerBytes));
            headerContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
            var content = new MultipartFormDataContent
            {
                { headerContent, "metadata", csvFileName }
            };
            request.Content = content;

            var response = await client.SendAsync(request);
        }
    }
}

Solution

  • You're not using HttpClient quite right. Notice how you declare the HttpRequestMessage - where you add the auth header - but never actually use it? You're sort of mixing the old method with the new one and not actually sending out the auth token.

    If you want to use HttpRequestMessage, which is fine, just use HttpClient.SendAsync and supply the message. Specifically try this after byte[] headerBytes = Encoding.UTF8.GetBytes(header);:

          request.Headers.Add("Content-Type", "application/octet-stream");
          var content = new MultipartFormDataContent();                  
          content.Add(new StreamContent(new MemoryStream(headerBytes)), "metadata", csvFileName);
          request.Content = content;
          var response = await client.SendAsync(request);