3

I'm writing a Blazor web app to pull pipeline data from Azure DevOps. Up until now I've been using a PAT for the access, but I want users to be able to use their Azure AD account for the access instead of a PAT. I've added the Azure AD authentication via the Microsoft.AspNetCore.Authentication.AzureAD.UI library so that you have to log in with your Azure AD account to access the site, and that works.

Now that I have them logged in, I want to use that login for the VssConnection used to make the calls to the REST APIs. Currently I'm using a personal access token like this:

VssCredentials vssCredentials = new VssBasicCredential(string.Empty, PAT);
VssConnection vssConnection = new VssConnection(new Uri($"https://dev.azure.com/{Org}"), vssCredentials);

How do I change that to utilize the Azure AD login they are using so that they don't need to provide the PAT?

UPDATE

I think I'm really close on this. I found a library on Github called Microsoft.Identity.Web which appears to do what I want. I think I'm just struggling to understand how do make the calls with the right scopes. So in my startup I now have:

services.AddMicrosoftIdentityPlatformAuthentication(Configuration)
                .AddMsal(Configuration, scopes)
                .AddInMemoryTokenCaches();

where "scopes" is an array of strings. And later I have:

string token = await _tokenAcquisition.GetAccessTokenOnBehalfOfUserAsync(scopes);

where _tokenAcquisition is an ITokenAcquisition from Microsoft.Identity.Web. And again, scopes is an array of strings. I'm not sure what scopes I'm supposed to use for the two calls. In my application registration in Azure Portal, I have

Application Registration

So what scopes do I use for the call in Startup, and what do I use in the call later to get the token? I've tried so many options that I can't even mention them all. Some fail on the call in Startup. Some fail in the token acquisition. Some get all the way through and then tell me I can't access dev.azure.com. Any help would be GREATLY appreciated.

BrianM
  • 836
  • 9
  • 17

5 Answers5

1

Currently, Azure DevOps doesn't provide OAuth library for the Asp.net Core web app. You can refer the exits authentication samples from link below:

Choosing the right authentication mechanism

However we can implement OAuth 2.0 ourselves by following Authorize access to REST APIs with OAuth 2.0. You need to put a sign-in button on the web page and compose the authorization request by the apps you register for Azure DevOps, and handle the response and get the access token. After that you can use the token to call the Azure DevOps REST.

The OAuth for Azure DevOps is different with authentication using Azure AD, it is separate OAuth provide by Azure DevOps directly. Below is the different authorization and token url between Azure DevOps and Azure AD:

Azure DevOps(OAuth 2.0)

Authorize URL: https://app.vssps.visualstudio.com/oauth2/authorize

Access Token URL: https://app.vssps.visualstudio.com/oauth2/token

Azure AD(OAuth Code Grant flow)

Authorize URL: https://login.microsoftonline.com/{tenant}/oauth2/authorize

Access Token URL: https://login.microsoftonline.com/{tenant}/oauth2/token

I would suggest you vote and leave your feedback from oAuth for DevOps from Asp.Net Core app (3.0) if you like Microsoft provide the authentication library and code sample for Asp.net Core.

Fei Xue
  • 14,369
  • 1
  • 19
  • 27
1

This worked for me:

string token = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { "499b84ac-1321-427f-aa17-267ca6975798/.default" });
VssCredentials creds = new VssAadCredential(new VssAadToken("Bearer", token));

Here is the StackOverflow question which contains an answer with the correct scope: How to get valid AAD v2 token using MSAL.js for Azure DevOps

0

If you are looking to use client libraries to access azure devops rest api, you can check this repo like below:

//Prompt user for credential
VssConnection connection = new VssConnection(new Uri(azureDevOpsOrganizationUrl), new VssClientCredentials());

//create http client and query for resutls
WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>();
Wiql query = new Wiql() 
             { 
                Query = "SELECT [Id], [Title], [State] FROM workitems WHERE [Work Item Type] = 'Bug' AND [Assigned To] = @Me" 
             };
WorkItemQueryResult queryResults = witClient.QueryByWiqlAsync(query).Result;

//Display reults in console
if (queryResults == null || queryResults.WorkItems.Count() == 0)
{
    Console.WriteLine("Query did not find any results");
}
else
{
    foreach (var item in queryResults.WorkItems)
    {
        Console.WriteLine(item.Id);
    }
}

But most optimum way for fetching information For native applications which can support interactive authentication prompts is the Azure Active Directory Authentication Library (ADAL) makes it easy to setup authentication flows for users (Considering you are developing a web application which is AD authenticated).

Prerequisite:

  • A user account in your AAD tenant

  • A Azure DevOps account backed by your AAD tenant where your user account has access. If you have an existing Azure DevOps account not connected to your AAD tenant follow these steps to connect your AAD tenant to your Azure DevOps account

  • Register the sample application with you Azure Active Directory tenant (AAD backed Azure DevOps account). You can follow this doc to setup the application.

  • you can check below github sample for the elaborated sample: Sample

Check this for additional reference:

How to get user token silently for Azure DevOps and use it for accessing DevOps REST APIs?

Hope it helps.

Mohamad Mousheimish
  • 1,641
  • 3
  • 16
  • 48
Mohit Verma
  • 5,140
  • 2
  • 12
  • 27
  • I've already done all of your prerequisites. The app prompts for an Azure account, authenticates you against our company AAD and only shows the page if you are successful. However, now that you can access the page, it wants to pull data from Azure DevOps. I've gone through the samples at [link](https://github.com/microsoft/azure-devops-auth-samples) but nothing is sticking out to me as the right solution. – BrianM Jan 12 '20 at 12:46
0

Have to log in with your Azure AD account to access the site.

If I did not have misunderstand, here it should pop up a dialog to allow users input their account info(username and password) and login in.

But, what the issue is, for our Azure Devops .Net Client library, its .NET Core version does not support the interactive sign-in prompt. Only .NET Framework version of the clients has this feature. But, unfortunately, what you are using is .net core.

Doc: sign-in prompt (Microsoft Account or Azure Active Directory backed) for REST services:

Because interactive dialogs are not supported by the .NET Core version of the clients, this sample applies only to the .NET Framework version of the clients.


Update:

 public class Program
     {
        // Do not change this id which fixed for azure devops.
        private const string VstsResourceId = "499b84ac-1321-427f-aa17-267ca6975798";
        public static void Main(string[] args)
        {
            var username = "{pass username here}"; 
            var password = "{pass password here}"; 
            var aadApplicationID = "{the application ID}"; 
            var adalCredential = new UserPasswordCredential(username, password);// make use of ADAL SDK
            var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/common");
            var result = authenticationContext.AcquireTokenAsync(VstsResourceId, aadApplicationID, adalCredential).Result;
            var token = new VssAadToken(result);
            var vstsCredential = new VssAadCredential(token);
            var connection = new VssConnection(new Uri("https://dev.azure.com/{org name}"), vstsCredential);
            var client = connection.GetClient<DelegatedAuthorizationHttpClient>();
            var pat = client.CreateSessionToken(
                displayName: "PAT Generate",
                tokenType: SessionTokenType.Compact,
                scope: "vso.work"
                ).Result;
           //print the token to verify the script
            Console.WriteLine(pat.Token);
        }
    }

For application id, please go check this doc to finish the register. .

Mengdi Liang
  • 17,577
  • 2
  • 28
  • 35
  • 1
    @merlin-liang-msft - Thank you for the reply. Yes, I am aware that there is no interactive login for .NET Core for Azure DevOps. But there is one to authenticate against Azure AAD. I'm using Microsoft.AspNetCore.Authentication.AzureAD.UI and it works perfectly in my Blazor app. The user is prompted for their login and can only see my site if they authenticate against AAD. My question is around how I take this authenticated login and (if possible) us it to hit the DevOps REST services without a PAT. – BrianM Jan 13 '20 at 10:15
  • @BrianM Based on my known, users cannot hit the azure devops without PAT. What I ever tried is generate the AAD token first, and then use this authorized token to make a request to azure devops. – Mengdi Liang Jan 13 '20 at 10:37
  • @merlin-liang-msft - I think we are really close here! So how do I call AquireTokenAsync with a Credential object that doesn't require a username and password? They've already provided their username/password to AAD to get onto the site, so is there some way to retrieve the token with their current credentials? – BrianM Jan 13 '20 at 15:35
  • @BrianM, Nope, the `adalCredential` is necessary for `AcquireTokenAsync`. Because it represent the user and acquiring the token from azure devops. Seems you have trouble on passing the username and password which input from users to the `adalCredential`? – Mengdi Liang Jan 14 '20 at 01:45
  • @merlin-liang-msft - I'm still not sure you're understanding my situation. As you stated before, there is no interactive sign-on prompt for Azure DevOps for .net core. I'm using Microsoft.AspNetCore.Authentication.AzureAD.UI, which brings up the Microsoft login page, asks for my account, re-directs to the work login page, and prompt my phone authenticator for approval. Once I'm logged in, the page will load. It's at this point that I want to hit the Azure REST services, using the login they already provided to bring the page up. I don't want to prompt them a second time for user/password. – BrianM Jan 14 '20 at 02:22
  • @BrianM, ok. May I do the summary of your puzzle as it is best if the token that phone authorized can be used to AcquireTokenAsync directly? I am one of azure devops team, but not professional on identity. Let me do some code on my side, and give you my answer – Mengdi Liang Jan 14 '20 at 03:20
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/205904/discussion-between-brianm-and-merlin-liang-msft). – BrianM Jan 14 '20 at 04:11
  • @BrianM So what was your conclusion? I'm also working on the same task. – Lasithe Jun 14 '20 at 11:12
  • @Lasitha - unfortunately I never came to one and am still using PATs. Haven't had much time to work on it so I've just left it the way it is. Sorry :( – BrianM Jun 15 '20 at 18:26
  • @BrianM no problem. – Lasithe Jun 16 '20 at 06:57
0

Take a look at this Github repo...

https://github.com/Apollo3zehn/CryptoDrive

It is a Blazor Server-Side app that uses AAD for login and then calls Graph but it shows you how to get the auth code and then use that to get a token. The most relevant code is in Startup.cs and AzureAdAuthenticationBuilderExtensions.cs.

Just an FYI, that is not my repo or code. I just stumbled across trying to solve the same issue you have run into.