Home > Blockchain >  Azure DevOps API call returns 401
Azure DevOps API call returns 401

Time:03-10

I'm building a service in .NET 6 to interact with Azure DevOps APIs, but I continually get an 401 Unauthorized response on my single method. My personal access token does work when I run this through Powershell. And my EncodeToken method seems to work. Any ideas on why this blows up?

public class ADOService
    {
        private readonly HttpClient _httpClient;
        private readonly IConfiguration _config;
        private readonly string _token;
        private readonly string _rootUrl;
        private readonly string _project;

        public ADOService(IConfiguration configuration)
        {
            _config = configuration;
            _httpClient = new HttpClient();

            _token = _config.GetSection("AzureDevOpsApiSettings").GetValue<string>("ADOPersonalAccessToken");
            _rootUrl = _config.GetSection("AzureDevOpsApiSettings").GetValue<string>("ADORootUrl");
            _project = _config.GetSection("AzureDevOpsApiSettings").GetValue<string>("Project");

            if (string.IsNullOrEmpty(_token) || string.IsNullOrEmpty(_rootUrl) || string.IsNullOrEmpty(_project))
            {
                throw new Exception("ADO Service is not properly configured");
            }
        }

        public async Task<ExpandoObject> GetWorkItems()
        {
            SetHttpHeaders();
            HttpResponseMessage response = await _httpClient.GetAsync($"{_project}/_apis/wit/workitemsbatch?api-version=6.0");

            if (!response.IsSuccessStatusCode)
            {
                throw new ApplicationException("failed");
            }
            else
            {
                var content = await response.Content.ReadAsStringAsync();
                var workItems = JsonSerializer.Deserialize<ExpandoObject>(content);
                return workItems;
            }
        }

        private string EncodeToken(string token)
        {
            if (string.IsNullOrEmpty(token)) throw new ArgumentNullException("token");
            var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(token);
            return Convert.ToBase64String(plainTextBytes);
        }

        private void SetHttpHeaders()
        {
            _httpClient.BaseAddress = new Uri(_rootUrl);
            _httpClient.DefaultRequestHeaders.Accept.Clear();
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", EncodeToken(_token));
            _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        }
    }

CodePudding user response:

Looks like your PAT is not converted to a valid Base64 string.

You can reference the example here to change your code to convert the PAT to a Base64 string and add it to the request header.

public static async void GetProjects()
{
    try
    {
        var personalaccesstoken = "PAT_FROM_WEBSITE";

        using (HttpClient client = new HttpClient())
        {
            client.DefaultRequestHeaders.Accept.Add(
                new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
                Convert.ToBase64String(
                    System.Text.ASCIIEncoding.ASCII.GetBytes(
                        string.Format("{0}:{1}", "", personalaccesstoken))));

            using (HttpResponseMessage response = await client.GetAsync(
                        "https://dev.azure.com/{organization}/_apis/projects"))
            {
                response.EnsureSuccessStatusCode();
                string responseBody = await response.Content.ReadAsStringAsync();
                Console.WriteLine(responseBody);
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
}
  • Related