Skip to content

Commit 49f055d

Browse files
authored
Migrate ADO Samples from ADAL to MSAL (#69)
* Migrate ManagedClientConsoleAppSample from ADAL to MSAL
1 parent d71af31 commit 49f055d

File tree

8 files changed

+253
-157
lines changed

8 files changed

+253
-157
lines changed

DeviceProfileSample/App.config

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,15 @@
33
<startup>
44
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
55
</startup>
6+
<appSettings>
7+
<!--Azure AD App Registration details-->
8+
<add key="ida:Tenant" value="[Enter_the_tenant_Id_Here]" />
9+
<add key="ida:ClientId" value="[Enter_the_Application_Id_Here]" />
10+
11+
<add key="ida:AADInstance" value="https://login.microsoftonline.com/{0}/v2.0" />
12+
13+
<!--URL of Azure DevOps organization-->
14+
<add key="ado:OrganizationUrl" value="[Enter_URL_Azure_DevOps_organization]" />
15+
16+
</appSettings>
617
</configuration>

DeviceProfileSample/DeviceProfileSample.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
<AssemblyName>DeviceProfileSample</AssemblyName>
99
</PropertyGroup>
1010
<ItemGroup>
11-
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="3.16.0" />
12-
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
11+
<PackageReference Include="Microsoft.Identity.Client" Version="4.29.0" />
12+
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
13+
<PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" />
1314
</ItemGroup>
1415
<ItemGroup>
1516
<Reference Include="Microsoft.CSharp" />

DeviceProfileSample/Program.cs

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,115 @@
1-
using Microsoft.IdentityModel.Clients.ActiveDirectory;
1+
using Microsoft.Identity.Client;
22
using System;
3+
using System.Configuration;
4+
using System.Globalization;
35
using System.Linq;
46
using System.Net.Http;
57
using System.Net.Http.Headers;
8+
using System.Threading.Tasks;
69

710
namespace DeviceProfileSample
811
{
912
public class Program
1013
{
11-
//============= Config [Edit these with your settings] =====================
12-
internal const string vstsCollectionUrl = "https://myaccount.visualstudio.com"; //change to the URL of your VSTS account; NOTE: This must use HTTPS
13-
internal const string clientId = "872cd9fa-d31f-45e0-9eab-6e460a02d1f1"; //update this with your Application ID from step 2.6 (do not change this if you have an MSA backed account)
14-
//==========================================================================
14+
//
15+
// The Client ID is used by the application to uniquely identify itself to Azure AD.
16+
// The Tenant is the name or Id of the Azure AD tenant in which this application is registered.
17+
// The AAD Instance is the instance of Azure, for example public Azure or Azure China.
18+
// The Authority is the sign-in URL of the tenant.
19+
//
20+
internal static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
21+
internal static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
22+
internal static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
23+
internal static string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
1524

16-
internal const string VSTSResourceId = "499b84ac-1321-427f-aa17-267ca6975798"; //Static value to target VSTS. Do not change
25+
//Constant value to target Azure DevOps. Do not change
26+
internal static string[] scopes = new string[] { "499b84ac-1321-427f-aa17-267ca6975798/user_impersonation" };
1727

28+
//URL of your Azure DevOps account.
29+
internal static string azureDevOpsOrganizationUrl = ConfigurationManager.AppSettings["ado:OrganizationUrl"];
30+
31+
// The MSAL Public client app
32+
private static IPublicClientApplication application;
1833

19-
public static void Main(string[] args)
34+
public static async Task Main(string[] args)
2035
{
21-
AuthenticationContext ctx = GetAuthenticationContext(null);
22-
AuthenticationResult result = null;
2336
try
2437
{
25-
DeviceCodeResult codeResult = ctx.AcquireDeviceCodeAsync(VSTSResourceId, clientId).Result;
26-
Console.WriteLine("You need to sign in.");
27-
Console.WriteLine("Message: " + codeResult.Message + "\n");
28-
result = ctx.AcquireTokenByDeviceCodeAsync(codeResult).Result;
38+
var authResult = await SignInUserAndGetTokenUsingMSAL(scopes);
39+
40+
// Create authorization header of the form "Bearer {AccessToken}"
41+
var authHeader = authResult.CreateAuthorizationHeader();
2942

30-
var bearerAuthHeader = new AuthenticationHeaderValue("Bearer", result.AccessToken);
31-
ListProjects(bearerAuthHeader);
43+
ListProjects(authHeader);
3244
}
3345
catch (Exception ex)
3446
{
3547
Console.ForegroundColor = ConsoleColor.Red;
3648
Console.WriteLine("Something went wrong.");
3749
Console.WriteLine("Message: " + ex.Message + "\n");
3850
}
51+
Console.ReadLine();
3952
}
4053

41-
private static AuthenticationContext GetAuthenticationContext(string tenant)
54+
/// <summary>
55+
/// Signs in the user using the device code flow and obtain an access token for Azure DevOps
56+
/// </summary>
57+
/// <param name="configuration"></param>
58+
/// <param name="scopes"></param>
59+
/// <returns>AuthenticationResult</returns>
60+
private static async Task<AuthenticationResult> SignInUserAndGetTokenUsingMSAL(string[] scopes)
4261
{
43-
AuthenticationContext ctx = null;
44-
if (tenant != null)
45-
ctx = new AuthenticationContext("https://login.microsoftonline.com/" + tenant);
46-
else
62+
// Initialize the MSAL library by building a public client application
63+
application = PublicClientApplicationBuilder.Create(clientId)
64+
.WithAuthority(authority)
65+
.WithDefaultRedirectUri()
66+
.Build();
67+
68+
69+
AuthenticationResult result;
70+
71+
try
72+
{
73+
var accounts = await application.GetAccountsAsync();
74+
// Try to acquire an access token from the cache. If device code is required, Exception will be thrown.
75+
result = await application.AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync();
76+
}
77+
catch (MsalUiRequiredException)
4778
{
48-
ctx = new AuthenticationContext("https://login.windows.net/common");
49-
if (ctx.TokenCache.Count > 0)
79+
result = await application.AcquireTokenWithDeviceCode(scopes, deviceCodeResult =>
5080
{
51-
string homeTenant = ctx.TokenCache.ReadItems().First().TenantId;
52-
ctx = new AuthenticationContext("https://login.microsoftonline.com/" + homeTenant);
53-
}
81+
// This will print the message on the console which tells the user where to go sign-in using
82+
// a separate browser and the code to enter once they sign in.
83+
// The AcquireTokenWithDeviceCode() method will poll the server after firing this
84+
// device code callback to look for the successful login of the user via that browser.
85+
// This background polling (whose interval and timeout data is also provided as fields in the
86+
// deviceCodeCallback class) will occur until:
87+
// * The user has successfully logged in via browser and entered the proper code
88+
// * The timeout specified by the server for the lifetime of this code (typically ~15 minutes) has been reached
89+
// * The developing application calls the Cancel() method on a CancellationToken sent into the method.
90+
// If this occurs, an OperationCanceledException will be thrown (see catch below for more details).
91+
Console.WriteLine(deviceCodeResult.Message);
92+
return Task.FromResult(0);
93+
}).ExecuteAsync();
5494
}
55-
56-
return ctx;
95+
return result;
5796
}
5897

59-
private static void ListProjects(AuthenticationHeaderValue authHeader)
98+
/// <summary>
99+
/// Get all projects in the organization that the authenticated user has access to and print the results.
100+
/// </summary>
101+
/// <param name="authHeader"></param>
102+
private static void ListProjects(string authHeader)
60103
{
61104
// use the httpclient
62105
using (var client = new HttpClient())
63106
{
64-
client.BaseAddress = new Uri(vstsCollectionUrl);
107+
client.BaseAddress = new Uri(azureDevOpsOrganizationUrl);
65108
client.DefaultRequestHeaders.Accept.Clear();
66109
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
67110
client.DefaultRequestHeaders.Add("User-Agent", "VstsRestApiSamples");
68111
client.DefaultRequestHeaders.Add("X-TFS-FedAuthRedirect", "Suppress");
69-
client.DefaultRequestHeaders.Authorization = authHeader;
112+
client.DefaultRequestHeaders.Add("Authorization", authHeader);
70113

71114
// connect to the REST endpoint
72115
HttpResponseMessage response = client.GetAsync("_apis/projects?stateFilter=All&api-version=2.2").Result;

DeviceProfileSample/README.md

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
# Device Profile Sample
22

3-
For a headless text output client application, it is not possible authenticate through an interactive prompt. Instead a text only approach is necessary. This flow leverages a user's external device (i.e. phone) to authenticate through an interactive login prompt and pass the auth token to the headless application. For more information [click here](https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-deviceprofile/?v=17.23h).
3+
For a headless text output client application, it is not possible authenticate through an interactive prompt. Instead a text only approach is necessary. This flow leverages a user's external device (i.e. phone) to authenticate through an interactive login prompt and pass the auth token to the headless application. For more information [click here](https://azure.microsoft.com/resources/samples/active-directory-dotnet-deviceprofile/?v=17.23h).
4+
5+
If the tenant admin requires device authentication conditional access policies, using the Device profile flow won't be a good option.
46

57
## Sample Application
68

7-
This buildable sample will walk you through the steps to create a client-side console application which uses ADAL to authenticate a user via the [Device Profile flow](https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-deviceprofile/?v=17.23h) and returns a JSON string containing all account team project data viewable by the authenticated user.
9+
This sample will walk you through the steps to create a client-side console application which uses **MSAL.NET** to authenticate a user via the [Device Profile flow](https://azure.microsoft.com/resources/samples/active-directory-dotnet-deviceprofile/?v=17.23h) and returns a JSON string containing all account team project data viewable by the authenticated user.
10+
11+
To run this sample you will need:
812

9-
To run this sample for an [Azure Active Directory](https://docs.microsoft.com/en-us/azure/active-directory/active-directory-whatis) backed Azure DevOps account you will need:
10-
* [Visual Studio IDE](https://www.visualstudio.com/vs/)
11-
* An Azure Active Directory (AAD) tenant. If you do not have one, follow these [steps to set up an AAD](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-howto-tenant)
12-
* A user account in your AAD tenant
13-
* 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](https://www.visualstudio.com/en-us/docs/setup-admin/team-services/manage-organization-access-for-your-account-vs)
13+
- [Visual Studio](https://visualstudio.microsoft.com/downloads/)
14+
- An **Azure AD** tenant. For more information see: [How to get an Azure AD tenant](https://docs.microsoft.com/azure/active-directory/develop/quickstart-create-new-tenant)
15+
- A user account in your **Azure AD** tenant.
16+
- 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](https://docs.microsoft.com/azure/devops/organizations/accounts/manage-azure-active-directory-groups-vsts?view=vsts&tabs=new-nav)
1417

1518
To run this sample for a [Microsoft Account](https://account.microsoft.com/account) backed Azure DevOps account you will need:
16-
* [Visual Studio IDE](https://www.visualstudio.com/vs/)
17-
* A Azure DevOps account not connected to AAD
1819

19-
## Step 1: Clone or download vsts-auth-samples repository
20+
- Azure DevOps account not connected to AAD.
2021

21-
From a shell or command line:
22-
```no-highlight
22+
## Step 1: Clone or download this repository
23+
24+
From a shell or command line:
25+
26+
```console
2327
git clone https://github.com/Microsoft/vsts-auth-samples.git
2428
```
2529

@@ -29,33 +33,35 @@ git clone https://github.com/Microsoft/vsts-auth-samples.git
2933
If you are a Microsoft Account backed Azure DevOps account please skip this step.
3034
```
3135

32-
1. Sign in to the [Azure Portal](https://portal.azure.com).
33-
2. On the top bar, click on your account and under the Directory list, choose the Active Directory tenant where you wish to register your application.
34-
3. On the left hand navigation menu, select `Azure Active Directory`.
35-
4. Click on `App registrations` and select `New application registration` from the top bar.
36-
5. Enter a `name` for you application, ex. "Adal native app sample", choose `Native` for `application type`, and enter `http://adalsample` for the `Redirect URI`. Finally click `create` at the bottom of the screen.
37-
6. Save the `Application ID` from your new application registration. You will need it later in this sample.
38-
7. Grant permissions for Azure DevOps. Click `Required permissions` -> `add` -> `1 Select an API` -> type in and select `Azure DevOps` -> check the box for `Delegated Permissions` -> click `Select` -> click `Done` -> click `Grant Permissions` -> click `Yes`.
36+
1. Navigate to the Microsoft identity platform for developers [App registrations](https://go.microsoft.com/fwlink/?linkid=2083908) page.
37+
1. Select **New registration**.
38+
1. In the **Register an application page** that appears, enter your application's registration information:
39+
- In the **Name** section, enter a meaningful application name that will be displayed to users of the app, for example `MSAL-DeviceCodeFlow`.
40+
- Under **Supported account types**, select **Accounts in this organizational directory only**.
41+
1. Select **Register** to create the application.
42+
1. In the app's registration screen, find and note the **Application (client) ID**. You use this value in your app's configuration file(s) later in your code.
43+
- In the **Advanced settings** | **Default client type** section, flip the switch for `Treat application as a public client` to **Yes**.
44+
1. Select **Save** to save your changes.
45+
1. In the app's registration screen, select the **API permissions** blade in the left to open the page where we add access to the APIs that your application needs.
46+
- Select the **Add a permission** button and then,
47+
- Ensure that the **Microsoft APIs** tab is selected.
48+
- In the list of APIs, select the API `Azure DevOps`.
49+
- In the **Delegated permissions** section, select the **user_impersonation** in the list. Use the search box if necessary.
50+
- Select the **Add permissions** button at the bottom.
51+
52+
## Step 3: Configure the application to use your app registration
3953

40-
## Step 3: Install and configure ADAL (optional)
54+
Open the project in your IDE (like Visual Studio or Visual Studio Code) to configure the code.
4155

42-
Package: `Microsoft.Identity.Model.Clients.ActiveDirectory` has already been installed and configured in the sample, but if you are adding to your own project you will need to [install and configure it yourself](https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory).
56+
> In the steps below, "ClientID" is the same as "Application ID" or "AppId".
4357
44-
## Step 4a: Run the sample (AAD backed Azure DevOps account)
58+
1. Open the `DeviceProfileSample\App.config` file.
59+
1. Find the key `ida:ClientID` and replace the existing value with the application ID (clientId) of `ManagedClientConsoleAppSample` app copied from the Azure portal.
60+
1. Find the key `ida:Tenant` and replace the existing value with your Azure AD tenant ID or tenant domain.
61+
1. Find the key `ado:OrganizationUrl` and replace the existing value to the URL of your Azure DevOps organization; NOTE: This must use HTTPS.
4562

46-
1. Navigate to the sample in cloned repo `vsts-auth-samples/DeviceProfileSample/`
47-
2. Open the solution file `DeviceProfileSample.sln` in [Visual Studio 2017](https://www.visualstudio.com/downloads/)
48-
3. Use [Nuget package restore](https://docs.microsoft.com/en-us/nuget/consume-packages/package-restore) to ensure you have all dependencies installed
49-
4. Open CS file `Program.cs` and there is a section with input values to change at the top of the class:
50-
* `azureDevOpsOrganizationUrl` - Update this value to your VSTS collection URL, e.g. http://dev.azure.com/organization.
51-
* `clientId` - Update this value with the `Application ID` you saved in step 2.6.
52-
5. Build and run solution. You should see a console window with instructions on how to authenticate via the Device Profile flow. After authenticating you should see all team project information viewable by the authenticated identity displayed in the console window.
63+
## Running the sample
5364

54-
## Step 4b: Run the sample (Microsoft Account backed Azure DevOps account)
65+
Clean the solution, rebuild the solution, and run it.
5566

56-
1. Navigate to the sample in cloned repo `vsts-auth-samples/DeviceProfileSample/`
57-
2. Open the solution file `DeviceProfileSample.sln` in [Visual Studio 2017](https://www.visualstudio.com/downloads/)
58-
3. Use [Nuget package restore](https://docs.microsoft.com/en-us/nuget/consume-packages/package-restore) to ensure you have all dependencies installed
59-
4. Open CS file `Program.cs` and there is a section with input values to change at the top of the class:
60-
* `azureDevOpsOrganizationUrl` - Update this value to your VSTS collection URL, e.g. http://dev.azure.com/organization.
61-
5. Build and run solution. You should see a console window with instructions on how to authenticate via the Device Profile flow. After authenticating you should see all team project information viewable by the authenticated identity displayed in the console window.
67+
Use a web browser to open the Url (https://microsoft.com/devicelogin) that is displayed in console app. Input the code presented in the console , sign-in and check the result of the operation back in the console.

ManagedClientConsoleAppSample/App.config

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,14 @@
3232
<appSettings>
3333
<!-- Service Bus specific app setings for messaging connections -->
3434
<add key="Microsoft.ServiceBus.ConnectionString" value="Endpoint=sb://[your namespace].servicebus.windows.net;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=[your secret]" />
35+
36+
<!--Azure AD App Registration details-->
37+
<add key="ida:Tenant" value="[Enter_the_tenant_Id_Here]" />
38+
<add key="ida:ClientId" value="[Enter_the_Application_Id_Here]" />
39+
40+
<add key="ida:AADInstance" value="https://login.microsoftonline.com/{0}/v2.0" />
41+
42+
<!--URL of Azure DevOps organization-->
43+
<add key="ado:OrganizationUrl" value="http://dev.azure.com/organization" />
3544
</appSettings>
3645
</configuration>

0 commit comments

Comments
 (0)