2

I am attempting to access the BitBucket API with C#. I can perform some actions, but not others. Notably, writing to repositories works, but reading them does not.

using System.Net;
using System.Collections.Specialized;

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; // TLS v1.2 only
var client = new WebClient()
{
    Credentials = new NetworkCredential("user", "app_password"),
    BaseAddress = "https://api.bitbucket.org",
};

client.DownloadString(
    "/2.0/repositories/friendly_private_account/repo");     // 403 Forbidden
client.DownloadString(
    "/2.0/repositories/friendly_private_account/repo/src"); // 403 Forbidden
client.UploadValues(
    "/2.0/repositories/friendly_private_account/repo/src",
    new NameValueCollection() {
        { "/bb.txt", "here is\nsome content\n" },
        { "message", "Commit from API, called with C# WebClient" },
    });                                                     // Creates a commit! What!?

This is kind of odd, since you get the read permission automatically if you enable the write permission when you create an App Password.

It's not a problem with DownloadString(), either. If the App Password has the webhook permission, you can read the Web Hooks.

client.DownloadString(
    "/2.0/repositories/friendly_private_account/repo/hooks");
// {"pagelen": 10, "values": [{ … }]}

Interestingly, curl does not have any trouble with the same credentials.

$ curl --user "${user}:${app_password}" \
       --url "https://api.bitbucket.org/2.0/repositories/friendly_private_account/repo"
# {"scm": "git", "website": "", "has_wiki": false, … }

Running curl with --verbose will actually return headers that describe which permissions your credentials have and which permissions are required. In the example above, it requires repository, and I have repository:write. It does not say that I have repository:read, but the request is successful nonetheless.

Michael
  • 8,362
  • 6
  • 61
  • 88
  • Really weird you can write but not read. Anyway not 100% sure, it might be a security protocol issue try enabling either TLS 1.1 and/or TLS 1.2, if you have not already. System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; I would also recommend to add UserAgent – Hasta Tamang Feb 14 '19 at 20:21
  • I tried explicitly using TLS v1.2, with no change. – Michael Feb 14 '19 at 20:45
  • Setting the User Agent to `curl/7.54.0` doesn't help either. – Michael Feb 14 '19 at 22:21

1 Answers1

0

It sounds like WebClient only sends the Authorization header when the server responds with a 401 Unauthorized.

Perhaps BitBucket responds with a 401 on some unauthenticated endpoints, provoking WebClient to resend the request with authentication; but returns a 403 on others, ending the request immediately.

Explicitly adding the Authorization header fixes things up, albeit a little ugly.

using System.Net;
using System.Collections.Specialized;

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;  // TLS v1.2 only
var client = new WebClient()
{
    // Credentials = new NetworkCredential("user", "app_password"), // Take this line out
    BaseAddress = "https://api.bitbucket.org",
};

client.Headers[HttpRequestHeader.Authorization] =
    "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes("user:app_password"));

client.DownloadString(
    "/2.0/repositories/friendly_private_account/repo");             // Now it works
Michael
  • 8,362
  • 6
  • 61
  • 88