diff --git a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/DeveloperCredentials/DeveloperCredentialsReader.cs b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/DeveloperCredentials/DeveloperCredentialsReader.cs index 8f68ef780..bf47e07cb 100644 --- a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/DeveloperCredentials/DeveloperCredentialsReader.cs +++ b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/DeveloperCredentials/DeveloperCredentialsReader.cs @@ -8,7 +8,7 @@ namespace Microsoft.DotNet.MSIdentity.DeveloperCredentials { public class DeveloperCredentialsReader { - public TokenCredential GetDeveloperCredentials(string? username, string? currentApplicationTenantId, IConsoleLogger consoleLogger) + public TokenCredential GetDeveloperCredentials(string? username, string? currentApplicationTenantId, string? instance, IConsoleLogger consoleLogger) { #if AzureSDK * Tried but does not work if another tenant than the home tenant id is specified @@ -30,6 +30,7 @@ public TokenCredential GetDeveloperCredentials(string? username, string? current TokenCredential tokenCredential = new MsalTokenCredential( currentApplicationTenantId, username, + instance, consoleLogger); return tokenCredential; } diff --git a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/DeveloperCredentials/MsalTokenCredential.cs b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/DeveloperCredentials/MsalTokenCredential.cs index 6c85a0585..c95fb29f7 100644 --- a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/DeveloperCredentials/MsalTokenCredential.cs +++ b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/DeveloperCredentials/MsalTokenCredential.cs @@ -25,12 +25,13 @@ public class MsalTokenCredential : TokenCredential public MsalTokenCredential( string? tenantId, string? username, + string? instance, IConsoleLogger consoleLogger) { _consoleLogger = consoleLogger; TenantId = tenantId ?? "organizations"; // MSA-passthrough Username = username; - Instance = "https://login.microsoftonline.com"; + Instance = instance ?? "https://login.microsoftonline.com"; // default instance } private IPublicClientApplication? App { get; set; } @@ -71,6 +72,7 @@ private async Task GetOrCreateApp() .Build(); App = PublicClientApplicationBuilder.Create(clientId) + .WithAuthority(Instance, TenantId) .WithRedirectUri(RedirectUri) .Build(); diff --git a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs index 22f7e4522..92070234a 100644 --- a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs +++ b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs @@ -12,7 +12,6 @@ using Microsoft.DotNet.MSIdentity.Properties; using Microsoft.DotNet.MSIdentity.Shared; using Microsoft.DotNet.MSIdentity.Tool; -using Microsoft.DotNet.Scaffolding.Shared; using Microsoft.Graph; namespace Microsoft.DotNet.MSIdentity.MicrosoftIdentityPlatformApplication diff --git a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/ProjectDescription/ProjectDescriptionReader.cs b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/ProjectDescription/ProjectDescriptionReader.cs index e19ed0594..c91a3a81d 100644 --- a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/ProjectDescription/ProjectDescriptionReader.cs +++ b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/ProjectDescription/ProjectDescriptionReader.cs @@ -41,7 +41,7 @@ public ProjectDescriptionReader(IEnumerable files) } // If projectDescription cannot be inferred, default to Web App - return ProjectDescriptions.FirstOrDefault(p => string.Equals(ProjectTypes.WebApp, p.Identifier)); + return ProjectDescriptions.FirstOrDefault(p => string.Equals($"{ProjectTypeIdPrefix}{ProjectTypes.WebApp}", p.Identifier)); } static readonly JsonSerializerOptions serializerOptionsWithComments = new JsonSerializerOptions diff --git a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/ProjectDescriptions/dotnet_webapp.json b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/ProjectDescriptions/dotnet_webapp.json index dccd80e67..0f92e9956 100644 --- a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/ProjectDescriptions/dotnet_webapp.json +++ b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/ProjectDescriptions/dotnet_webapp.json @@ -3,6 +3,10 @@ "ProjectRelativeFolder": "", "BasedOnProjectDescription": "dotnet-web", "MatchesForProjectType": [ + { + "FileRelativePath": "Startup.cs", + "MatchAny": [ ".AddAzureAD", ".AddMicrosoftIdentityWebApp", ".AddMicrosoftIdentityWebAppAuthentication", "Microsoft.Owin" ] + }, { "FolderRelativePath": "Pages", "FileRelativePath": "Index.cshtml", diff --git a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Properties/Resources.Designer.cs b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Properties/Resources.Designer.cs index 7189d5987..cb1fd10ae 100644 --- a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Properties/Resources.Designer.cs +++ b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Properties/Resources.Designer.cs @@ -644,7 +644,7 @@ internal static string FailedToProvisionClientApp { } /// - /// Looks up a localized string similar to Failed to retrieve all Azure AD/AD B2C objects(apps/service principals. + /// Looks up a localized string similar to Failed to retrieve all Azure AD/AD B2C objects (apps/service principals), exception: {0}. /// internal static string FailedToRetrieveADObjectsError { get { diff --git a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Properties/Resources.resx b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Properties/Resources.resx index 73d20cf61..fce3fc0e4 100644 --- a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Properties/Resources.resx +++ b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Properties/Resources.resx @@ -269,7 +269,8 @@ Failed to provision Client Application for Blazor WASM hosted project - Failed to retrieve all Azure AD/AD B2C objects(apps/service principals + Failed to retrieve all Azure AD/AD B2C objects (apps/service principals), exception: {0} + 0 = error message Failed to retrieve application parameters. @@ -347,4 +348,4 @@ Updating project packages ... - \ No newline at end of file + diff --git a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/AppProvisioningTool.cs b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/AppProvisioningTool.cs index 50f5e829e..bd0c0dd69 100644 --- a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/AppProvisioningTool.cs +++ b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/AppProvisioningTool.cs @@ -235,6 +235,7 @@ internal static TokenCredential GetTokenCredential(ProvisioningToolOptions provi return developerCredentialsReader.GetDeveloperCredentials( provisioningToolOptions.Username, currentApplicationTenantId ?? provisioningToolOptions.TenantId, + provisioningToolOptions.Instance, consoleLogger); } diff --git a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/GraphObjectRetriever.cs b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/GraphObjectRetriever.cs index 0e5ffb9f6..66f53a139 100644 --- a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/GraphObjectRetriever.cs +++ b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/GraphObjectRetriever.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -66,17 +65,17 @@ public GraphObjectRetriever(GraphServiceClient graphServiceClient, IConsoleLogge nextPage = null; } } - catch (ServiceException) + catch (ServiceException se) { nextPage = null; - _consoleLogger.LogFailureAndExit(Resources.FailedToRetrieveADObjectsError); + _consoleLogger.LogFailureAndExit(string.Format(Resources.FailedToRetrieveADObjectsError, se.Message)); } } } } - catch (ServiceException) + catch (ServiceException se) { - _consoleLogger.LogFailureAndExit(Resources.FailedToRetrieveADObjectsError); + _consoleLogger.LogFailureAndExit(string.Format(Resources.FailedToRetrieveADObjectsError, se.Message)); } return graphObjectsList; diff --git a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/MsAADTool.cs b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/MsAADTool.cs index 69000a633..ed6d7a437 100644 --- a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/MsAADTool.cs +++ b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/MsAADTool.cs @@ -30,7 +30,7 @@ public MsAADTool(string commandName, ProvisioningToolOptions provisioningToolOpt ProvisioningToolOptions = provisioningToolOptions; CommandName = commandName; ConsoleLogger = new ConsoleLogger(CommandName, ProvisioningToolOptions.Json); - TokenCredential = new MsalTokenCredential(ProvisioningToolOptions.TenantId, ProvisioningToolOptions.Username, ConsoleLogger); + TokenCredential = new MsalTokenCredential(ProvisioningToolOptions.TenantId, ProvisioningToolOptions.Username, ProvisioningToolOptions.Instance, ConsoleLogger); GraphServiceClient = new GraphServiceClient(new TokenCredentialAuthenticationProvider(TokenCredential)); AzureManagementAPI = new AzureManagementAuthenticationProvider(TokenCredential); GraphObjectRetriever = new GraphObjectRetriever(GraphServiceClient, ConsoleLogger); @@ -91,13 +91,9 @@ internal async Task PrintApplicationsList() internal async Task> GetApplicationsAsync() { - var graphObjectsList = await GraphObjectRetriever.GetGraphObjects(); - if (graphObjectsList is null) - { - ConsoleLogger.LogFailureAndExit(Resources.FailedToRetrieveADObjectsError); - } - IList applicationList = new List(); + + var graphObjectsList = await GraphObjectRetriever.GetGraphObjects(); // Will exit early if call fails foreach (var graphObj in graphObjectsList!) { if (graphObj is Application app) diff --git a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/ProvisioningToolOptions.cs b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/ProvisioningToolOptions.cs index e8c2b9091..3e8cd1704 100644 --- a/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/ProvisioningToolOptions.cs +++ b/src/MSIdentityScaffolding/Microsoft.DotNet.MSIdentity/Tool/ProvisioningToolOptions.cs @@ -101,6 +101,12 @@ public string ProjectTypeIdentifier /// public string? TenantId { get; set; } + /// + /// URL that indicates a directory that MSAL can request tokens from. + /// e.g. https://login.microsoftonline.com/, https://login.microsoftonline.us/ + /// + public string? Instance { get; set; } + /// /// Required for the creation of a B2C application. /// Represents the sign-up/sign-in user flow. diff --git a/tools/dotnet-msidentity/Program.cs b/tools/dotnet-msidentity/Program.cs index 57d594fae..0fd7a8f09 100644 --- a/tools/dotnet-msidentity/Program.cs +++ b/tools/dotnet-msidentity/Program.cs @@ -180,7 +180,7 @@ internal static Command ListAADAppsCommand() => name: Commands.LIST_AAD_APPS_COMMAND, description: "Lists AAD Applications for a given tenant/username.\n") { - TenantOption(), UsernameOption(), JsonOption() + TenantOption(), UsernameOption(), InstanceOption(), JsonOption() }; internal static Command ListServicePrincipalsCommand() => @@ -188,7 +188,7 @@ internal static Command ListServicePrincipalsCommand() => name: Commands.LIST_SERVICE_PRINCIPALS_COMMAND, description: "Lists AAD Service Principals.\n") { - TenantOption(), UsernameOption(), JsonOption() + TenantOption(), UsernameOption(), InstanceOption(), JsonOption() }; internal static Command ListTenantsCommand() => @@ -204,7 +204,7 @@ internal static Command CreateClientSecretCommand() => name: Commands.ADD_CLIENT_SECRET, description: "Create client secret for an Azure AD or AD B2C app registration.\n") { - TenantOption(), UsernameOption(), JsonOption(), ClientIdOption(), ProjectFilePathOption(), UpdateUserSecretsOption() + TenantOption(), UsernameOption(), InstanceOption(), JsonOption(), ClientIdOption(), ProjectFilePathOption(), UpdateUserSecretsOption() }; internal static Command RegisterApplicationCommand() => @@ -213,7 +213,7 @@ internal static Command RegisterApplicationCommand() => description: "Register an Azure AD or Azure AD B2C app registration in Azure and update the project." + "\n\t- Updates the appsettings.json file.\n") { - TenantOption(), UsernameOption(), JsonOption(), ClientIdOption(), ClientSecretOption(), HostedAppIdUriOption(), ApiClientIdOption(), SusiPolicyIdOption(), ProjectFilePathOption() + TenantOption(), UsernameOption(), InstanceOption(), JsonOption(), ClientIdOption(), ClientSecretOption(), HostedAppIdUriOption(), ApiClientIdOption(), SusiPolicyIdOption(), ProjectFilePathOption() }; internal static Command UpdateProjectCommand() => @@ -224,7 +224,7 @@ internal static Command UpdateProjectCommand() => "\n\t- Updates the Startup.cs file." + "\n\t- Updates the user secrets.\n") { - TenantOption(), UsernameOption(), ClientIdOption(), JsonOption(), ProjectFilePathOption(), ConfigUpdateOption(), CodeUpdateOption(), PackagesUpdateOption(), CallsGraphOption(), CallsDownstreamApiOption(), UpdateUserSecretsOption(), RedirectUriOption(), SusiPolicyIdOption() + TenantOption(), UsernameOption(), InstanceOption(), ClientIdOption(), JsonOption(), ProjectFilePathOption(), ConfigUpdateOption(), CodeUpdateOption(), PackagesUpdateOption(), CallsGraphOption(), CallsDownstreamApiOption(), UpdateUserSecretsOption(), RedirectUriOption(), SusiPolicyIdOption() }; internal static Command UpdateAppRegistrationCommand() => @@ -232,7 +232,7 @@ internal static Command UpdateAppRegistrationCommand() => name: Commands.UPDATE_APP_REGISTRATION_COMMAND, description: "Update an Azure AD/AD B2C app registration in Azure.\n") { - TenantOption(), UsernameOption(), JsonOption(), HostedAppIdUriOption(), ClientIdOption(), RedirectUriOption(), EnableIdTokenOption(), EnableAccessToken(), ClientProjectOption(), ApiScopesOption() + TenantOption(), UsernameOption(), InstanceOption(), JsonOption(), HostedAppIdUriOption(), ClientIdOption(), RedirectUriOption(), EnableIdTokenOption(), EnableAccessToken(), ClientProjectOption(), ApiScopesOption() }; internal static Command CreateAppRegistrationCommand() => @@ -240,7 +240,7 @@ internal static Command CreateAppRegistrationCommand() => name: Commands.CREATE_APP_REGISTRATION_COMMAND, description: "Create an Azure AD/AD B2C app registration in Azure.\n") { - TenantOption(), UsernameOption(), JsonOption(), AppDisplayName(), ProjectFilePathOption(), ProjectType(), ClientProjectOption() + TenantOption(), UsernameOption(), InstanceOption(), JsonOption(), AppDisplayName(), ProjectFilePathOption(), ProjectType(), ClientProjectOption() }; internal static Command UnregisterApplicationCommand() => @@ -250,7 +250,7 @@ internal static Command UnregisterApplicationCommand() => description: "Unregister an Azure AD or Azure AD B2C app registration in Azure." + "\n\t- Updates the appsettings.json file.\n") { - TenantOption(), UsernameOption(), JsonOption(), HostedAppIdUriOption(), ProjectFilePathOption(), ClientIdOption() + TenantOption(), UsernameOption(), InstanceOption(), JsonOption(), HostedAppIdUriOption(), ProjectFilePathOption(), ClientIdOption() }; private static Option JsonOption() => @@ -435,5 +435,15 @@ private static Option UsernameOption() => { IsRequired = false }; + + + private static Option InstanceOption() => + new Option( + aliases: new[] { "-i", "--instance" }, + description: "Instance where the Azure AD or Azure AD B2C tenant is located.\n" + + "If not specified, will default to https://login.microsoftonline.com/") + { + IsRequired = false + }; } }