Dhillli4u
Dhillli4u

Reputation: 139

Execution works with personal access token, but not using AAD access token for Azure DevOps

I have my below code which output the master branch stats in JSON format from Azure DevOps repository and I am capturing the required output. This works when I use the personal access token the authentication works and gets back with the results from the API.

But when I try to generate Access token using the registered app in AAD(has delegated user impersonation enabled for Azure DevOps under API permissions), I am able to generate the access token and then passing it while calling the API, but it returns back with

StatusCode: 203, ReasonPhrase: 'Non-Authoritative Information', Version: 1.1, Content: System.Net.Http.StreamContent

public static async Task GetBuilds()
{
    string url = "Azure Dev-Ops API";
    var personalaccesstoken = "personalaccesscode";
    //var personalaccesstoken = token.GetYourTokenWithClientCredentialsFlow().Result;
    string value = null;

using (HttpClient client = new HttpClient())
{
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "", personalaccesstoken))));
    using (HttpResponseMessage response = await client.GetAsync(url))
    {
        response.EnsureSuccessStatusCode();
        string responseBody = await response.Content.ReadAsStringAsync();
        dynamic jsonObject = JsonConvert.DeserializeObject(responseBody);
        value = jsonObject;
    }
}

if (value != null)
{
    Console.WriteLine(value);
}
}

public static async Task<string> GetYourTokenWithClientCredentialsFlow()
{
    string tokenUrl = $"https://login.microsoftonline.com/{tenant ID}/oauth2/token";
    var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl);

    tokenRequest.Content = new FormUrlEncodedContent(new Dictionary<string, string>
    {
        ["grant_type"] = "client_credentials",
        ["client_id"] = "client ID",
        ["client_secret"] = "client secret",
        ["resource"] = "https://graph.microsoft.com/"
    });

    dynamic json;
    dynamic token;
    string accessToken;

    HttpClient client = new HttpClient();

    var tokenResponse = client.SendAsync(tokenRequest).Result;
    json = await tokenResponse.Content.ReadAsStringAsync();
    token = JsonConvert.DeserializeObject(json);
    accessToken = token.access_token;
    return accessToken;
}

Tried to test using postman using the access token generated using above code and get as below screenshot.

enter image description here

what I am doing wrong here and how can I fix the problem?

Upvotes: 1

Views: 1802

Answers (1)

Jack Jia
Jack Jia

Reputation: 5549

The azure ad access token is a bearer token. You do not need to use it as basic auth.

Try with the following code:

client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", GetYourTokenWithClientCredentialsFlow().Result);

Update:

  1. Register a new app enter image description here

  2. Set the app as a public client by default enter image description here

  3. Add permission to DevOps API enter image description here

  4. Create a new project, install Microsoft.IdentityModel.Clients.ActiveDirectory package enter image description here

  5. Code sample

class Program
{
    static string azureDevOpsOrganizationUrl = "https://dev.azure.com/jack0503/"; //change to the URL of your Azure DevOps account; NOTE: This must use HTTPS
    static string clientId = "0a1f****-****-****-****-a2a4****7f69";          //change to your app registration's Application ID
    static string replyUri = "https://localhost/";                     //change to your app registration's reply URI
    static string azureDevOpsResourceId = "499b84ac-1321-427f-aa17-267ca6975798"; //Constant value to target Azure DevOps. Do not change  
    static string tenant = "hanxia.onmicrosoft.com";     //your tenant ID or Name
    static String GetTokenInteractively()
    {
        AuthenticationContext ctx = new AuthenticationContext("https://login.microsoftonline.com/" + tenant); ;
        IPlatformParameters promptBehavior = new PlatformParameters(PromptBehavior.Auto | PromptBehavior.SelectAccount);
        AuthenticationResult result = ctx.AcquireTokenAsync(azureDevOpsResourceId, clientId, new Uri(replyUri), promptBehavior).Result;
        return result.AccessToken;
    }

    static String GetToken()
    {
        AuthenticationContext ctx = new AuthenticationContext("https://login.microsoftonline.com/" + tenant); ;
        UserPasswordCredential upc = new UserPasswordCredential("[email protected]", "yourpassword");
        AuthenticationResult result = ctx.AcquireTokenAsync(azureDevOpsResourceId, clientId, upc).Result;
        return result.AccessToken;
    }
    static void Main(string[] args)
    {
        //string token = GetTokenInteractively();
        string token = GetToken();

        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri(azureDevOpsOrganizationUrl);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

            HttpResponseMessage response = client.GetAsync("_apis/projects").Result;

            if (response.IsSuccessStatusCode)
            {
                Console.WriteLine("\tSuccesful REST call");
                var result = response.Content.ReadAsStringAsync().Result;
                Console.WriteLine(result);
            }
            else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
            {
                throw new UnauthorizedAccessException();
            }
            else
            {
                Console.WriteLine("{0}:{1}", response.StatusCode, response.ReasonPhrase);
            }

            Console.ReadLine();
        }
    }
}

Upvotes: 3

Related Questions