Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Core;
using Microsoft.DotNet.MSIdentity.Shared;

namespace Microsoft.DotNet.MSIdentity.DeveloperCredentials
{
public class DeveloperCredentialsReader
{
public TokenCredential GetDeveloperCredentials(string? username, string? currentApplicationTenantId)
public TokenCredential GetDeveloperCredentials(string? username, string? currentApplicationTenantId, IConsoleLogger consoleLogger)
{
#if AzureSDK
* Tried but does not work if another tenant than the home tenant id is specified
Expand All @@ -28,7 +29,8 @@ public TokenCredential GetDeveloperCredentials(string? username, string? current
#endif
TokenCredential tokenCredential = new MsalTokenCredential(
currentApplicationTenantId,
username);
username,
consoleLogger);
return tokenCredential;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Core;
using Microsoft.Graph;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.Extensions.Msal;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
using Microsoft.DotNet.MSIdentity.Shared;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.Extensions.Msal;

namespace Microsoft.DotNet.MSIdentity.DeveloperCredentials
{
Expand All @@ -19,11 +19,17 @@ public class MsalTokenCredential : TokenCredential
private const string RedirectUri = "http://localhost";
#pragma warning restore S1075 // URIs should not be hardcoded

public MsalTokenCredential(string? tenantId, string? username, string instance = "https://login.microsoftonline.com")
private readonly IConsoleLogger _consoleLogger;

public MsalTokenCredential(
string? tenantId,
string? username,
IConsoleLogger consoleLogger)
{
_consoleLogger = consoleLogger;
TenantId = tenantId ?? "organizations"; // MSA-passthrough
Username = username;
Instance = instance;
Instance = "https://login.microsoftonline.com";
}

private IPublicClientApplication? App { get; set; }
Expand Down Expand Up @@ -99,7 +105,9 @@ public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext r
{
if (account == null && !string.IsNullOrEmpty(Username))
{
Console.WriteLine($"No valid tokens found in the cache.\nPlease sign-in to Visual Studio with this account:\n\n{Username}.\n\nAfter signing-in, re-run the tool.\n");
LogError($"No valid tokens found in the cache.\n" +
$"Please sign-in to Visual Studio with this account: {Username}.\n\n" +
$"After signing-in, re-run the tool.");
}
result = await app.AcquireTokenInteractive(requestContext.Scopes)
.WithAccount(account)
Expand All @@ -111,22 +119,26 @@ public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext r
{
if (ex.Message.Contains("AADSTS70002")) // "The client does not exist or is not enabled for consumers"
{
Console.WriteLine("An Azure AD tenant, and a user in that tenant, " +
"needs to be created for this account before an application can be created. See https://aka.ms/ms-identity-app/create-a-tenant. ");
LogError("An Azure AD tenant, and a user in that tenant, " +
"needs to be created for this account before an application can be created. " +
"See https://aka.ms/ms-identity-app/create-a-tenant. ");
Environment.Exit(1); // we want to exit here because this is probably an MSA without an AAD tenant.
}

Console.WriteLine("Error encountered with sign-in. See error message for details:\n{0} ",
ex.Message);
LogError("Error encountered with sign-in. See error message for details:\n{ex.Message}");
Environment.Exit(1); // we want to exit here. Re-sign in will not resolve the issue.
}
catch (Exception ex)
{
Console.WriteLine("Error encountered with sign-in. See error message for details:\n{0} ",
ex.Message);
LogError($"Error encountered with sign-in. See error message for details:\n{ex.Message}");
Environment.Exit(1);
}
return new AccessToken(result.AccessToken, result.ExpiresOn);
}

private void LogError(string message)
{
_consoleLogger.LogJsonMessage(new JsonResponse(null, State.Fail, output: message));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Core;
Expand Down Expand Up @@ -31,7 +31,6 @@ public TokenCredentialAuthenticationProvider(
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
// Try with the Shared token cache credentials

TokenRequestContext context = new TokenRequestContext(_initialScopes.ToArray());
AccessToken token = await _tokenCredentials.GetTokenAsync(context, CancellationToken.None);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ public AppProvisioningTool(string commandName, ProvisioningToolOptions provision
// Get developer credentials
TokenCredential tokenCredential = GetTokenCredential(
ProvisioningToolOptions,
ProvisioningToolOptions.TenantId ?? projectSettings.ApplicationParameters.EffectiveTenantId ?? projectSettings.ApplicationParameters.EffectiveDomain);
ProvisioningToolOptions.TenantId ?? projectSettings.ApplicationParameters.EffectiveTenantId ?? projectSettings.ApplicationParameters.EffectiveDomain,
ConsoleLogger);

ApplicationParameters applicationParameters;
switch (CommandName)
Expand Down Expand Up @@ -230,12 +231,13 @@ private ProjectAuthenticationSettings InferApplicationParameters(
/// <param name="provisioningToolOptions"></param>
/// <param name="currentApplicationTenantId"></param>
/// <returns></returns>
internal static TokenCredential GetTokenCredential(ProvisioningToolOptions provisioningToolOptions, string? currentApplicationTenantId)
internal static TokenCredential GetTokenCredential(ProvisioningToolOptions provisioningToolOptions, string? currentApplicationTenantId, IConsoleLogger consoleLogger)
{
DeveloperCredentialsReader developerCredentialsReader = new DeveloperCredentialsReader();
return developerCredentialsReader.GetDeveloperCredentials(
provisioningToolOptions.Username,
currentApplicationTenantId ?? provisioningToolOptions.TenantId);
currentApplicationTenantId ?? provisioningToolOptions.TenantId,
consoleLogger);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.DotNet.MSIdentity.Properties;
using Microsoft.DotNet.MSIdentity.Shared;
using Microsoft.Graph;

namespace Microsoft.DotNet.MSIdentity.Tool
{
public interface IGraphObjectRetriever
{
/// <summary>
/// Requests all directory objects from the graph service client for the given account
/// </summary>
/// <returns></returns>
public Task<List<DirectoryObject>?> GetGraphObjects();

/// <summary>
/// Retrieves the Tenant object associated with the GraphServiceClient
/// </summary>
/// <returns></returns>
public Task<Organization?> GetTenant();
}

public class GraphObjectRetriever : IGraphObjectRetriever
{
private readonly GraphServiceClient _graphServiceClient;
private readonly IConsoleLogger _consoleLogger;

public GraphObjectRetriever(GraphServiceClient graphServiceClient, IConsoleLogger consoleLogger)
{
_graphServiceClient = graphServiceClient;
_consoleLogger = consoleLogger;
}

public async Task<List<DirectoryObject>?> GetGraphObjects()
{
List<DirectoryObject> graphObjectsList = new List<DirectoryObject>();
try
{
var graphObjects = await _graphServiceClient.Me.OwnedObjects
.Request()
.GetAsync();

if (graphObjects != null)
{
graphObjectsList.AddRange(graphObjects.ToList());

var nextPage = graphObjects.NextPageRequest;
while (nextPage != null)
{
try
{
var additionalGraphObjects = await nextPage.GetAsync();
if (additionalGraphObjects != null)
{
graphObjectsList.AddRange(additionalGraphObjects.ToList());
nextPage = additionalGraphObjects.NextPageRequest;
}
else
{
nextPage = null;
}
}
catch (ServiceException)
{
nextPage = null;
_consoleLogger.LogMessage(Resources.FailedToRetrieveADObjectsError, LogMessageType.Error);
return null;
}
}
}
}
catch (ServiceException)
{
_consoleLogger.LogMessage(Resources.FailedToRetrieveADObjectsError, LogMessageType.Error);
return null;
}

return graphObjectsList;
}

public async Task<Organization?> GetTenant()
{
Organization? tenant;
try
{
tenant = (await _graphServiceClient.Organization
.Request()
.GetAsync()).FirstOrDefault();
}
catch (ServiceException ex)
{
string? errorMessage;
if (ex.InnerException != null)
{
errorMessage = ex.InnerException.Message;
}
else
{
if (ex.Message.Contains("User was not found") || ex.Message.Contains("not found in tenant"))
{
errorMessage = "User was not found.\nUse both --tenant-id <tenant> --username <username@tenant>.\nAnd re-run the tool.";
}
else
{
errorMessage = ex.Message;
}
}

_consoleLogger.LogJsonMessage(new JsonResponse(null, State.Fail, output:errorMessage));
return null;
}

return tenant;
}
}
}
Loading