-
Notifications
You must be signed in to change notification settings - Fork 300
/
Program.cs
134 lines (120 loc) · 6.37 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
using Microsoft.Identity.Client;
using System;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace DeviceProfileSample
{
public class Program
{
//
// The Client ID is used by the application to uniquely identify itself to Azure AD.
// The Tenant is the name or Id of the Azure AD tenant in which this application is registered.
// The AAD Instance is the instance of Azure, for example public Azure or Azure China.
// The Authority is the sign-in URL of the tenant.
//
internal static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
internal static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
internal static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
internal static string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
//Constant value to target Azure DevOps. Do not change
internal static string[] scopes = new string[] { "499b84ac-1321-427f-aa17-267ca6975798/user_impersonation" };
//URL of your Azure DevOps account.
internal static string azureDevOpsOrganizationUrl = ConfigurationManager.AppSettings["ado:OrganizationUrl"];
// The MSAL Public client app
private static IPublicClientApplication application;
public static async Task Main(string[] args)
{
try
{
var authResult = await SignInUserAndGetTokenUsingMSAL(scopes);
// Create authorization header of the form "Bearer {AccessToken}"
var authHeader = authResult.CreateAuthorizationHeader();
ListProjects(authHeader);
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Something went wrong.");
Console.WriteLine("Message: " + ex.Message + "\n");
}
Console.ReadLine();
}
/// <summary>
/// Signs in the user using the device code flow and obtain an access token for Azure DevOps
/// </summary>
/// <param name="configuration"></param>
/// <param name="scopes"></param>
/// <returns>AuthenticationResult</returns>
private static async Task<AuthenticationResult> SignInUserAndGetTokenUsingMSAL(string[] scopes)
{
// Initialize the MSAL library by building a public client application
application = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(authority)
.WithDefaultRedirectUri()
.Build();
AuthenticationResult result;
try
{
var accounts = await application.GetAccountsAsync();
// Try to acquire an access token from the cache. If device code is required, Exception will be thrown.
result = await application.AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync();
}
catch (MsalUiRequiredException)
{
result = await application.AcquireTokenWithDeviceCode(scopes, deviceCodeResult =>
{
// This will print the message on the console which tells the user where to go sign-in using
// a separate browser and the code to enter once they sign in.
// The AcquireTokenWithDeviceCode() method will poll the server after firing this
// device code callback to look for the successful login of the user via that browser.
// This background polling (whose interval and timeout data is also provided as fields in the
// deviceCodeCallback class) will occur until:
// * The user has successfully logged in via browser and entered the proper code
// * The timeout specified by the server for the lifetime of this code (typically ~15 minutes) has been reached
// * The developing application calls the Cancel() method on a CancellationToken sent into the method.
// If this occurs, an OperationCanceledException will be thrown (see catch below for more details).
Console.WriteLine(deviceCodeResult.Message);
return Task.FromResult(0);
}).ExecuteAsync();
}
return result;
}
/// <summary>
/// Get all projects in the organization that the authenticated user has access to and print the results.
/// </summary>
/// <param name="authHeader"></param>
private static void ListProjects(string authHeader)
{
// use the httpclient
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(azureDevOpsOrganizationUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("User-Agent", "VstsRestApiSamples");
client.DefaultRequestHeaders.Add("X-TFS-FedAuthRedirect", "Suppress");
client.DefaultRequestHeaders.Add("Authorization", authHeader);
// connect to the REST endpoint
HttpResponseMessage response = client.GetAsync("_apis/projects?stateFilter=All&api-version=2.2").Result;
// check to see if we have a succesfull respond
if (response.IsSuccessStatusCode)
{
Console.WriteLine("\tSuccesful REST call");
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
}
else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
throw new UnauthorizedAccessException();
}
else
{
Console.WriteLine("{0}:{1}", response.StatusCode, response.ReasonPhrase);
}
}
}
}
}