From 6997a4bbaa90c4a55a22846d0c9a4ce875d9eed3 Mon Sep 17 00:00:00 2001 From: Deep Choudhery Date: Fri, 9 Apr 2021 14:58:58 -0700 Subject: [PATCH 1/5] initial commit --- eng/Versions.MsIdentity.props | 7 - eng/Versions.props | 6 +- .../ApplicationParameters.cs | 12 +- .../AzureAdProperties.cs | 25 +++ .../CodeReaderWriter/CodeWriter.cs | 108 +++++++--- .../Microsoft.DotNet.MsIdentity.csproj | 19 +- ...osoftIdentityPlatformApplicationManager.cs | 63 +++--- .../ProjectDescriptionReader.cs | 2 +- .../Tool/AppProvisioningTool.cs | 194 ++++++++++++++++-- .../Tool/ProvisioningToolOptions.cs | 25 ++- tools/dotnet-msidentity/Program.cs | 33 ++- 11 files changed, 410 insertions(+), 84 deletions(-) create mode 100644 src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs diff --git a/eng/Versions.MsIdentity.props b/eng/Versions.MsIdentity.props index c04c6a6ce..fe25b4145 100644 --- a/eng/Versions.MsIdentity.props +++ b/eng/Versions.MsIdentity.props @@ -5,13 +5,6 @@ false true - - - $(MicrosoftBuildPackageVersion) - $(MicrosoftBuildPackageVersion) - $(MicrosoftBuildPackageVersion) - - 1.0.0 preview diff --git a/eng/Versions.props b/eng/Versions.props index da90281a7..438146fc8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -30,6 +30,8 @@ 2.2.0 16.8.0 + + 16.8.0 16.8.0 @@ -46,7 +48,7 @@ 6.0.0-preview.3.21201.4 6.0.0-preview.3.21201.4 - 11.0.2 + 13.0.1 9.0.1 5.0.0 @@ -135,6 +137,8 @@ 2.18.0 4.7.2 2.0.0-beta1.20574.7 + 3.9.0 + 3.9.0 diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/ApplicationParameters.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/ApplicationParameters.cs index dd6bb9a62..047f57030 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/ApplicationParameters.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/ApplicationParameters.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; @@ -16,6 +16,11 @@ public class ApplicationParameters /// public string? ApplicationDisplayName { get; set; } + /// + /// Application project path + /// + public string? ProjectPath { get; set; } + /// /// Tenant in which the application is created. /// @@ -179,6 +184,11 @@ public string? Domain1 /// public string? MsalAuthenticationOptions { get; set; } + /// + /// Graph.Application and Graph.ServicePrincipal object ids. + /// + public string? GraphEntityId { get; set; } + /// /// Sets a bool property (from its name). /// diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs new file mode 100644 index 000000000..d3dabe497 --- /dev/null +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.DotNet.MsIdentity.AuthenticationParameters +{ + public class AzureAdProperties + { + public string? Domain { get; set; } + public string? TenantId { get; set; } + public string? ClientId { get; set; } + public string? ClientSecret { get; set; } + public string? CallbackPath { get; set; } + public string? Instance { get; set; } + } + + public static class AzureAdDefaultProperties + { + public const string Domain = "qualified.domain.name"; + public const string ClientId = "22222222-2222-2222-2222-222222222222"; + public const string TenantId = "11111111-1111-1111-11111111111111111"; + public const string Instance = "https://login.microsoftonline.com/"; + public const string CallbackPath = "/signin-oidc"; + } +} diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/CodeReaderWriter/CodeWriter.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/CodeReaderWriter/CodeWriter.cs index 7ce503746..4f945afcc 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/CodeReaderWriter/CodeWriter.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/CodeReaderWriter/CodeWriter.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. - -using Microsoft.DotNet.MsIdentity.AuthenticationParameters; -using Microsoft.DotNet.MsIdentity.Project; using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.DotNet.MsIdentity.AuthenticationParameters; +using Microsoft.DotNet.MsIdentity.Project; +using Microsoft.Extensions.Internal; namespace Microsoft.DotNet.MsIdentity.CodeReaderWriter { - public class CodeWriter + public static class CodeWriter { - internal void WriteConfiguration(Summary summary, IEnumerable replacements, ApplicationParameters reconcialedApplicationParameters) + internal static void WriteConfiguration(Summary summary, IEnumerable replacements, ApplicationParameters reconcialedApplicationParameters) { foreach (var replacementsInFile in replacements.GroupBy(r => r.FilePath)) { @@ -49,36 +49,86 @@ internal void WriteConfiguration(Summary summary, IEnumerable repla } } - private string? ComputeReplacement(string replaceBy, ApplicationParameters reconciledApplicationParameters) + public static void AddUserSecrets(bool isB2C, string projectPath, string value) + { + //init regardless. If it's already initiated, dotnet-user-secrets confirms it. + InitUserSecrets(projectPath); + string section = isB2C ? "AzureADB2C" : "AzureAD"; + string key = $"{section}:ClientSecret"; + SetUserSecerets(projectPath, key, value); + } + private static void InitUserSecrets(string projectPath) + { + var errors = new List(); + var output = new List(); + + IList arguments = new List(); + + //if project path is present, use it for dotnet user-secrets + if (!string.IsNullOrEmpty(projectPath)) + { + arguments.Add("-p"); + arguments.Add(projectPath); + } + + arguments.Add("init"); + var result = Command.CreateDotNet( + "user-secrets", + arguments.ToArray()) + .OnErrorLine(e => errors.Add(e)) + .OnOutputLine(o => output.Add(o)) + .Execute(); + + if (result.ExitCode != 0) + { + throw new Exception("Error while running dotnet-user-secrets init"); + } + } + + private static void SetUserSecerets(string projectPath, string key, string value) + { + var errors = new List(); + var output = new List(); + + IList arguments = new List(); + + //if project path is present, use it for dotnet user-secrets + if (!string.IsNullOrEmpty(projectPath)) + { + arguments.Add("-p"); + arguments.Add(projectPath); + } + + arguments.Add("set"); + arguments.Add(key); + arguments.Add(value); + var result = Command.CreateDotNet( + "user-secrets", + arguments) + .OnErrorLine(e => errors.Add(e)) + .OnOutputLine(o => output.Add(o)) + .Execute(); + + if (result.ExitCode != 0) + { + throw new Exception($"Error while running dotnet-user-secrets set {key} {value}"); + } + else + { + Console.WriteLine($"\nAdded {key} to user secrets.\n"); + } + } + + private static string? ComputeReplacement(string replaceBy, ApplicationParameters reconciledApplicationParameters) { string? replacement = replaceBy; switch(replaceBy) { case "Application.ClientSecret": string? password = reconciledApplicationParameters.PasswordCredentials.LastOrDefault(); - if (!string.IsNullOrEmpty(reconciledApplicationParameters.SecretsId)) + if (!string.IsNullOrEmpty(reconciledApplicationParameters.SecretsId) && !string.IsNullOrEmpty(password)) { - // TODO: adapt for Linux: https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-5.0&tabs=windows#how-the-secret-manager-tool-works - string? envVariable = Environment.GetEnvironmentVariable("UserProfile"); - if (!string.IsNullOrEmpty(envVariable)) - { - string path = Path.Combine( - envVariable, - @"AppData\Roaming\Microsoft\UserSecrets\", - reconciledApplicationParameters.SecretsId, - "secrets.json")!; - if (!File.Exists(path)) - { - Directory.CreateDirectory(Path.GetDirectoryName(path)!); - string section = reconciledApplicationParameters.IsB2C ? "AzureADB2C" : "AzureAD"; - File.WriteAllText(path, $"{{\n \"{section}:ClientSecret\": \"{password}\"\n}}"); - replacement = "See user secrets"; - } - else - { - replacement = password; - } - } + AddUserSecrets(reconciledApplicationParameters.IsB2C, reconciledApplicationParameters.ProjectPath ?? string.Empty, password); } else { diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj index 795a9bc21..48c350266 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1;net5.0 @@ -32,9 +32,26 @@ + + + + + + + + + + + + Shared\%(RecursiveDir)%(FileName).cs + + + + diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs index a2c337075..98a601ca6 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs @@ -1,14 +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.AuthenticationParameters; -using Microsoft.Graph; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Threading.Tasks; +using Azure.Core; +using Microsoft.DotNet.MsIdentity.AuthenticationParameters; +using Microsoft.Graph; namespace Microsoft.DotNet.MsIdentity.MicrosoftIdentityPlatformApplication { @@ -123,7 +123,7 @@ await AddApiPermissionFromBlazorwasmHostedSpaToServerApi( { await AddPasswordCredentials( graphServiceClient, - createdApplication, + createdApplication.Id, effectiveApplicationParameters); } @@ -193,7 +193,7 @@ await graphServiceClient.Applications[existingApplication.Id] { await AddPasswordCredentials( graphServiceClient, - existingApplication, + existingApplication.Id, reconcialedApplicationParameters).ConfigureAwait(false); } } @@ -251,18 +251,22 @@ await graphServiceClient.Oauth2PermissionGrants /// /// /// - private static async Task AddPasswordCredentials(GraphServiceClient graphServiceClient, Application createdApplication, ApplicationParameters effectiveApplicationParameters) + internal static async Task AddPasswordCredentials(GraphServiceClient graphServiceClient, string applicatonId, ApplicationParameters effectiveApplicationParameters) { var passwordCredential = new PasswordCredential { - DisplayName = "Password created by the provisioning tool" + DisplayName = "Password created by dotnet-msidentity tool" }; - PasswordCredential returnedPasswordCredential = await graphServiceClient.Applications[$"{createdApplication.Id}"] - .AddPassword(passwordCredential) - .Request() - .PostAsync(); - effectiveApplicationParameters.PasswordCredentials.Add(returnedPasswordCredential.SecretText); + if (!string.IsNullOrEmpty(applicatonId) && graphServiceClient != null) + { + //change app id to id + PasswordCredential returnedPasswordCredential = await graphServiceClient.Applications[$"{applicatonId}"] + .AddPassword(passwordCredential) + .Request() + .PostAsync(); + effectiveApplicationParameters.PasswordCredentials.Add(returnedPasswordCredential.SecretText); + } } /// @@ -539,7 +543,7 @@ await graphServiceClient.Applications[$"{readApplication.Id}"] } } - private GraphServiceClient GetGraphServiceClient(TokenCredential tokenCredential) + internal GraphServiceClient GetGraphServiceClient(TokenCredential tokenCredential) { if (_graphServiceClient == null) { @@ -551,10 +555,25 @@ private GraphServiceClient GetGraphServiceClient(TokenCredential tokenCredential public async Task ReadApplication(TokenCredential tokenCredential, ApplicationParameters applicationParameters) { var graphServiceClient = GetGraphServiceClient(tokenCredential); - // Get the tenant Organization? tenant = await GetTenant(graphServiceClient); + var application = await GetApplication(tokenCredential, applicationParameters); + if (application != null) + { + + ApplicationParameters effectiveApplicationParameters = GetEffectiveApplicationParameters( + tenant!, + application, + applicationParameters); + + return effectiveApplicationParameters; + } + return null; + } + public async Task GetApplication(TokenCredential tokenCredential, ApplicationParameters applicationParameters) + { + var graphServiceClient = GetGraphServiceClient(tokenCredential); var apps = await graphServiceClient.Applications .Request() .Filter($"appId eq '{applicationParameters.ClientId}'") @@ -566,14 +585,7 @@ private GraphServiceClient GetGraphServiceClient(TokenCredential tokenCredential { return null; } - - ApplicationParameters effectiveApplicationParameters = GetEffectiveApplicationParameters( - tenant!, - readApplication, - applicationParameters); - - return effectiveApplicationParameters; - + return readApplication; } private ApplicationParameters GetEffectiveApplicationParameters( @@ -607,7 +619,8 @@ private ApplicationParameters GetEffectiveApplicationParameters( TargetFramework = originalApplicationParameters.TargetFramework, MsalAuthenticationOptions = originalApplicationParameters.MsalAuthenticationOptions, CalledApiScopes = originalApplicationParameters.CalledApiScopes, - AppIdUri = originalApplicationParameters.AppIdUri + AppIdUri = originalApplicationParameters.AppIdUri, + GraphEntityId = application.Id }; if (application.Api != null && application.IdentifierUris.Any()) diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/ProjectDescription/ProjectDescriptionReader.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/ProjectDescription/ProjectDescriptionReader.cs index 79ca46d0c..07ebaee6c 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/ProjectDescription/ProjectDescriptionReader.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/ProjectDescription/ProjectDescriptionReader.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -70,7 +71,6 @@ public class ProjectDescriptionReader { files = new string[0]; } - foreach (string filePath in files) { // If there are matches, one at least needs to match diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs index 6b8a7cdf2..05369e6e0 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs @@ -7,12 +7,14 @@ using System.Linq; using System.Threading.Tasks; using Azure.Core; +using Microsoft.CodeAnalysis; using Microsoft.DotNet.MsIdentity.Properties; using Microsoft.DotNet.MsIdentity.AuthenticationParameters; using Microsoft.DotNet.MsIdentity.CodeReaderWriter; using Microsoft.DotNet.MsIdentity.DeveloperCredentials; using Microsoft.DotNet.MsIdentity.MicrosoftIdentityPlatformApplication; using Microsoft.DotNet.MsIdentity.Project; +using Newtonsoft.Json.Linq; namespace Microsoft.DotNet.MsIdentity { @@ -42,7 +44,21 @@ public AppProvisioningTool(string commandName, ProvisioningToolOptions provision ProvisioningToolOptions.ProjectTypeIdentifier, ProvisioningToolOptions.ProjectPath); - //need different error formatting when the output format is json. + //get csproj file path + var csProjfiles = Directory.EnumerateFiles(ProvisioningToolOptions.ProjectPath, "*.csproj"); + if (csProjfiles.Any()) + { + var filePath = csProjfiles.First(); + ProvisioningToolOptions.ProjectCsProjPath = filePath; + } + //get appsettings.json file path + var appSettingsFile = Directory.EnumerateFiles(ProvisioningToolOptions.ProjectPath, "appsettings.json"); + if (appSettingsFile.Any()) + { + var filePath = appSettingsFile.First(); + ProvisioningToolOptions.AppSettingsFilePath = filePath; + } + if (projectDescription == null) { Console.WriteLine($"The code in {ProvisioningToolOptions.ProjectPath} wasn't recognized as supported by the tool. Rerun with --help for details."); @@ -52,12 +68,48 @@ public AppProvisioningTool(string commandName, ProvisioningToolOptions provision { Console.WriteLine($"Detected project type {projectDescription.Identifier}. "); } - + Debugger.Launch(); ProjectAuthenticationSettings projectSettings = InferApplicationParameters( ProvisioningToolOptions, projectDescription, ProjectDescriptionReader.projectDescriptions); + // Get developer credentials + TokenCredential tokenCredential = GetTokenCredential( + ProvisioningToolOptions, + projectSettings.ApplicationParameters.EffectiveTenantId ?? projectSettings.ApplicationParameters.EffectiveDomain); + + if (CommandName.Equals(Commands.UPDATE_PROJECT_COMMAND, StringComparison.OrdinalIgnoreCase)) + { + // Read or provision Microsoft identity platform application + ApplicationParameters? applicationParameters = await ReadOrProvisionMicrosoftIdentityApplication( + tokenCredential, + projectSettings.ApplicationParameters); + + if (applicationParameters != null) + { + ModifyAppSettings(applicationParameters); + //Add ClientSecret if the app wants to call graph/a downstream api. + if (ProvisioningToolOptions.CallsGraph || ProvisioningToolOptions.CallsDownstreamApi) + { + var graphServiceClient = MicrosoftIdentityPlatformApplicationManager.GetGraphServiceClient(tokenCredential); + if (graphServiceClient != null && !string.IsNullOrEmpty(applicationParameters.ClientId) && !string.IsNullOrEmpty(applicationParameters.GraphEntityId)) + { + await MicrosoftIdentityPlatformApplicationManager.AddPasswordCredentials( + graphServiceClient, + applicationParameters.GraphEntityId, + applicationParameters); + + string? password = applicationParameters.PasswordCredentials.LastOrDefault(); + if (!string.IsNullOrEmpty(password) && ProvisioningToolOptions.UpdateUserSecrets) + { + CodeWriter.AddUserSecrets(applicationParameters.IsB2C, ProvisioningToolOptions.ProjectPath, password); + } + } + } + } + return applicationParameters; + } // Case of a blazorwasm hosted application. We need to create two applications: // - the hosted web API // - the SPA. @@ -95,11 +147,6 @@ public AppProvisioningTool(string commandName, ProvisioningToolOptions provision $"be created, but the tool does not add the code yet (work in progress). "); } - // Get developer credentials - TokenCredential tokenCredential = GetTokenCredential( - ProvisioningToolOptions, - projectSettings.ApplicationParameters.EffectiveTenantId ?? projectSettings.ApplicationParameters.EffectiveDomain); - // Unregister the app if (CommandName.Equals(Commands.UNREGISTER_APPLICATION_COMMAND, StringComparison.OrdinalIgnoreCase)) { @@ -121,7 +168,7 @@ public AppProvisioningTool(string commandName, ProvisioningToolOptions provision projectSettings.ApplicationParameters, effectiveApplicationParameters); - // Update appp registration if needed + // Update app registration if needed if (appNeedsUpdate) { await WriteApplicationRegistration( @@ -143,6 +190,126 @@ await WriteApplicationRegistration( return effectiveApplicationParameters; } + // add 'MicrosoftGraph' or 'DownstreamAPI' section. + private void ModifyAppSettings(ApplicationParameters applicationParameters) + { + string? filePath = ProvisioningToolOptions.AppSettingsFilePath; + if (!string.IsNullOrEmpty(filePath)) + { + bool changesMade = false; + //waiting for https://github.com/dotnet/runtime/issues/29690 + https://github.com/dotnet/runtime/issues/31068 to switch over to System.Text.Json + JObject appSettings = JObject.Parse(File.ReadAllText(filePath)); + if (appSettings != null) + { + var azureAdToken = appSettings["AzureAd"]; + if (azureAdToken != null) + { + var azureAdProperty = azureAdToken.ToObject(); + if (azureAdProperty != null) + { + if (!string.IsNullOrEmpty(azureAdProperty.Domain) && + !azureAdProperty.Domain.Equals(applicationParameters.Domain, StringComparison.OrdinalIgnoreCase)) + { + changesMade = true; + azureAdToken["Domain"] = applicationParameters.Domain; + } + + if (!string.IsNullOrEmpty(azureAdProperty.TenantId) && + !azureAdProperty.TenantId.Equals(applicationParameters.TenantId, StringComparison.OrdinalIgnoreCase)) + { + changesMade = true; + azureAdToken["TenantId"] = applicationParameters.TenantId; + } + + if (!string.IsNullOrEmpty(azureAdProperty.ClientId) && + !azureAdProperty.ClientId.Equals(applicationParameters.ClientId, StringComparison.OrdinalIgnoreCase)) + { + changesMade = true; + azureAdToken["ClientId"] = applicationParameters.ClientId; + } + + if (!string.IsNullOrEmpty(azureAdProperty.Instance) && + !azureAdProperty.Instance.Equals(applicationParameters.Instance, StringComparison.OrdinalIgnoreCase)) + { + changesMade = true; + azureAdToken["Instance"] = applicationParameters.Instance ?? AzureAdDefaultProperties.Instance; + } + + if (!string.IsNullOrEmpty(azureAdProperty.CallbackPath) && + !azureAdProperty.CallbackPath.Equals(applicationParameters.CallbackPath, StringComparison.OrdinalIgnoreCase)) + { + changesMade = true; + azureAdToken["CallbackPath"] = applicationParameters.CallbackPath ?? AzureAdDefaultProperties.CallbackPath; + } + } + } + else + { + changesMade = true; + appSettings.Add("AzureAd", JObject.FromObject(new + { + Instance = applicationParameters.Instance ?? AzureAdDefaultProperties.Instance, + Domain = applicationParameters.Domain, + TenantId = applicationParameters.TenantId, + ClientId = applicationParameters.ClientId, + CallbackPath = applicationParameters.CallbackPath ?? AzureAdDefaultProperties.CallbackPath + })); + } + + if (ProvisioningToolOptions.CallsGraph || ProvisioningToolOptions.CallsDownstreamApi) + { + + if (azureAdToken != null) + { + if (azureAdToken["ClientSecret"] == null) + { + changesMade = true; + azureAdToken["ClientSecret"] = "Client secret from app-registration. Check user secrets/azure portal."; + } + + if (azureAdToken["ClientCertificates"] == null) + { + changesMade = true; + azureAdToken["ClientCertificates"] = new JArray(); + } + } + + if (ProvisioningToolOptions.CallsDownstreamApi) + { + if (appSettings["DownstreamApi"] == null) + { + changesMade = true; + string apiURL = !string.IsNullOrEmpty(ProvisioningToolOptions.CalledApiUrl) ? ProvisioningToolOptions.CalledApiUrl : "API_URL_HERE"; + appSettings.Add("DownstreamApi", JObject.FromObject(new + { BaseUrl = apiURL, + Scopes = "user.read" + })); + } + } + + if (ProvisioningToolOptions.CallsGraph) + { + if (appSettings["MicrosoftGraph"] == null) + { + changesMade = true; + appSettings.Add("MicrosoftGraph", JObject.FromObject(new + { + BaseUrl = "https://graph.microsoft.com/v1.0", + Scopes = "user.read" + })); + } + } + } + } + + + //save comments somehow, only write to appsettings.json if changes are made + if (appSettings != null && changesMade) + { + File.WriteAllText(filePath, appSettings.ToString()); + } + } + } /// /// Converts an AAD application to a B2C application /// @@ -206,8 +373,7 @@ private async Task WriteApplicationRegistration(Summary summary, ApplicationPara private void WriteProjectConfiguration(Summary summary, ProjectAuthenticationSettings projectSettings, ApplicationParameters reconcialedApplicationParameters) { - CodeWriter codeWriter = new CodeWriter(); - codeWriter.WriteConfiguration(summary, projectSettings.Replacements, reconcialedApplicationParameters); + CodeWriter.WriteConfiguration(summary, projectSettings.Replacements, reconcialedApplicationParameters); } private bool Reconciliate(ApplicationParameters applicationParameters, ApplicationParameters effectiveApplicationParameters) @@ -238,7 +404,7 @@ private bool Reconciliate(ApplicationParameters applicationParameters, Applicati ApplicationParameters applicationParameters) { ApplicationParameters? currentApplicationParameters = null; - if (!string.IsNullOrEmpty(applicationParameters.EffectiveClientId)) + if (!string.IsNullOrEmpty(applicationParameters.EffectiveClientId) || !string.IsNullOrEmpty(applicationParameters.ClientId)) { currentApplicationParameters = await MicrosoftIdentityPlatformApplicationManager.ReadApplication(tokenCredential, applicationParameters); if (currentApplicationParameters == null) @@ -265,9 +431,9 @@ private ProjectAuthenticationSettings InferApplicationParameters( // Override with the tools options projectSettings.ApplicationParameters.ApplicationDisplayName ??= Path.GetFileName(provisioningToolOptions.ProjectPath); - projectSettings.ApplicationParameters.ClientId ??= provisioningToolOptions.ClientId; - projectSettings.ApplicationParameters.TenantId ??= provisioningToolOptions.TenantId; - projectSettings.ApplicationParameters.CalledApiScopes ??= provisioningToolOptions.CalledApiScopes; + projectSettings.ApplicationParameters.ClientId = !string.IsNullOrEmpty(provisioningToolOptions.ClientId) ? provisioningToolOptions.ClientId : projectSettings.ApplicationParameters.ClientId; + projectSettings.ApplicationParameters.TenantId = !string.IsNullOrEmpty(provisioningToolOptions.TenantId) ? provisioningToolOptions.TenantId : projectSettings.ApplicationParameters.TenantId; + projectSettings.ApplicationParameters.CalledApiScopes = !string.IsNullOrEmpty(provisioningToolOptions.CalledApiScopes) ? provisioningToolOptions.CalledApiScopes : projectSettings.ApplicationParameters.CalledApiScopes; if (!string.IsNullOrEmpty(provisioningToolOptions.AppIdUri)) { projectSettings.ApplicationParameters.AppIdUri = provisioningToolOptions.AppIdUri; diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/ProvisioningToolOptions.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/ProvisioningToolOptions.cs index 9dfe2a811..c59cbb150 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/ProvisioningToolOptions.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/ProvisioningToolOptions.cs @@ -9,6 +9,16 @@ public class ProvisioningToolOptions : IDeveloperCredentialsOptions { public string ProjectPath { get; set; } = System.IO.Directory.GetCurrentDirectory(); + /// + /// Path to csproj file + /// + public string? ProjectCsProjPath { get; set; } + + /// + /// Path to appsettings.json file + /// + public string? AppSettingsFilePath { get; set; } + /// /// Language/Framework for the project. /// @@ -94,6 +104,16 @@ public string ProjectTypeIdentifier /// public bool CallsGraph { get; set; } + /// + /// Calls Downstream API + /// + public bool CallsDownstreamApi { get; set; } + + /// + /// Add secrets to user secrets.json file. + /// + public bool UpdateUserSecrets { get; set; } + /// /// The App ID Uri for the blazorwasm hosted API. It's only used /// on the case of a blazorwasm hosted application. @@ -109,13 +129,14 @@ public string ProjectTypeIdentifier /// Clones the options /// /// - public ProvisioningToolOptions Clone() { return new ProvisioningToolOptions() { CalledApiScopes = CalledApiScopes, CalledApiUrl = CalledApiUrl, + CallsDownstreamApi = CallsDownstreamApi, + UpdateUserSecrets = UpdateUserSecrets, CallsGraph = CallsGraph, ClientId = ClientId, ClientSecret = ClientSecret, @@ -127,6 +148,8 @@ public ProvisioningToolOptions Clone() Unregister = Unregister, Username = Username, ProjectPath = ProjectPath, + ProjectCsProjPath = ProjectCsProjPath, + AppSettingsFilePath = AppSettingsFilePath, WebApiClientId = WebApiClientId, AppIdUri = AppIdUri, Json = Json diff --git a/tools/dotnet-msidentity/Program.cs b/tools/dotnet-msidentity/Program.cs index 5646f2f8d..0b60ada0d 100644 --- a/tools/dotnet-msidentity/Program.cs +++ b/tools/dotnet-msidentity/Program.cs @@ -126,7 +126,7 @@ private static async Task HandleUpdateProject(ProvisioningToolOptions provi { if (provisioningToolOptions != null) { - IMsAADTool msAADTool = MsAADToolFactory.CreateTool(Commands.UPDATE_APPLICATION_COMMAND, provisioningToolOptions); + IMsAADTool msAADTool = MsAADToolFactory.CreateTool(Commands.UPDATE_PROJECT_COMMAND, provisioningToolOptions); await msAADTool.Run(); return 0; } @@ -180,7 +180,7 @@ private static Command UpdateProjectCommand()=> "\n\t- Updates the Startup.cs file." + "\n\t- Updates the user secrets.") { - TenantOption(), UsernameOption(), JsonOption(), ProjectPathOption(), ClientIdOption() + TenantOption(), UsernameOption(), JsonOption(), ProjectPathOption(), ClientIdOption(), CallsGraphOption(), CallsDownstreamApiOption(), UpdateUserSecrets() }; private static Command UpdateApplicationCommand() => @@ -189,7 +189,7 @@ private static Command UpdateApplicationCommand() => description: "Update an AAD/AAD B2C application in Azure." + "\n\t- Updates the appsettings.json file.") { - TenantOption(), UsernameOption(), JsonOption(), AppIdUriOption(), ClientIdOption(), ProjectPathOption() + TenantOption(), UsernameOption(), JsonOption(), AppIdUriOption(), ClientIdOption(), ProjectPathOption(), }; private static Command UnregisterApplicationCommand() => @@ -208,7 +208,32 @@ private static Option JsonOption()=> { IsRequired = false }; - + + private static Option CallsGraphOption()=> + new Option( + aliases: new [] {"-cg", "--calls-graph"}, + description: "App registration calls microsoft graph.") + { + IsRequired = false + }; + + private static Option CallsDownstreamApiOption()=> + new Option( + aliases: new [] {"-cda", "--calls-downstream-api"}, + description: "App registration calls downstream api.") + { + IsRequired = false + }; + + private static Option UpdateUserSecrets()=> + new Option( + aliases: new [] {"-uus", "--update-user-secrets"}, + description: "Add secrets to user secrets.json file." + + "\n\t- Using dotnet-user-secrets to init and set user secrets.") + { + IsRequired = false + }; + private static Option ClientIdOption()=> new Option( aliases: new [] {"-ci", "--client-id"}, From 7941eb32e2ada8ec569411187f6fc148493e3f2f Mon Sep 17 00:00:00 2001 From: Deep Choudhery Date: Mon, 12 Apr 2021 12:27:01 -0700 Subject: [PATCH 2/5] minor cleanup --- eng/Versions.props | 4 ---- .../AzureAdProperties.cs | 1 + .../Microsoft.DotNet.MsIdentity.csproj | 10 -------- ...osoftIdentityPlatformApplicationManager.cs | 2 -- .../ProjectDescriptionReader.cs | 1 - .../Tool/AppProvisioningTool.cs | 23 ++++++++++++------- 6 files changed, 16 insertions(+), 25 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 438146fc8..80906cf65 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -30,8 +30,6 @@ 2.2.0 16.8.0 - - 16.8.0 16.8.0 @@ -137,8 +135,6 @@ 2.18.0 4.7.2 2.0.0-beta1.20574.7 - 3.9.0 - 3.9.0 diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs index d3dabe497..e99eac279 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs @@ -14,6 +14,7 @@ public class AzureAdProperties public string? Instance { get; set; } } + //getting default properties from https://github.com/dotnet/aspnetcore/blob/6bc4b79f4ee7af00edcbb435e5ee4c1de349a110/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/appsettings.json public static class AzureAdDefaultProperties { public const string Domain = "qualified.domain.name"; diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj index 48c350266..250c972b9 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj @@ -33,16 +33,6 @@ - - - - - - - - - diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs index 98a601ca6..6cc58b5bc 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.Linq; using System.Threading.Tasks; @@ -260,7 +259,6 @@ internal static async Task AddPasswordCredentials(GraphServiceClient graphServic if (!string.IsNullOrEmpty(applicatonId) && graphServiceClient != null) { - //change app id to id PasswordCredential returnedPasswordCredential = await graphServiceClient.Applications[$"{applicatonId}"] .AddPassword(passwordCredential) .Request() diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/ProjectDescription/ProjectDescriptionReader.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/ProjectDescription/ProjectDescriptionReader.cs index 07ebaee6c..30db5a1a9 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/ProjectDescription/ProjectDescriptionReader.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/ProjectDescription/ProjectDescriptionReader.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs index 05369e6e0..5fbd58631 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs @@ -68,7 +68,7 @@ public AppProvisioningTool(string commandName, ProvisioningToolOptions provision { Console.WriteLine($"Detected project type {projectDescription.Identifier}. "); } - Debugger.Launch(); + ProjectAuthenticationSettings projectSettings = InferApplicationParameters( ProvisioningToolOptions, projectDescription, @@ -79,6 +79,8 @@ public AppProvisioningTool(string commandName, ProvisioningToolOptions provision ProvisioningToolOptions, projectSettings.ApplicationParameters.EffectiveTenantId ?? projectSettings.ApplicationParameters.EffectiveDomain); + //for now, update project command is handlded seperately. + //TODO: switch case to handle all the different commands. if (CommandName.Equals(Commands.UPDATE_PROJECT_COMMAND, StringComparison.OrdinalIgnoreCase)) { // Read or provision Microsoft identity platform application @@ -88,11 +90,14 @@ public AppProvisioningTool(string commandName, ProvisioningToolOptions provision if (applicationParameters != null) { + //modify appsettings.json. ModifyAppSettings(applicationParameters); + //Add ClientSecret if the app wants to call graph/a downstream api. if (ProvisioningToolOptions.CallsGraph || ProvisioningToolOptions.CallsDownstreamApi) { var graphServiceClient = MicrosoftIdentityPlatformApplicationManager.GetGraphServiceClient(tokenCredential); + //need ClientId and Microsoft.Graph.Application.Id(GraphEntityId) if (graphServiceClient != null && !string.IsNullOrEmpty(applicationParameters.ClientId) && !string.IsNullOrEmpty(applicationParameters.GraphEntityId)) { await MicrosoftIdentityPlatformApplicationManager.AddPasswordCredentials( @@ -101,6 +106,7 @@ await MicrosoftIdentityPlatformApplicationManager.AddPasswordCredentials( applicationParameters); string? password = applicationParameters.PasswordCredentials.LastOrDefault(); + //if user wants to update user secrets if (!string.IsNullOrEmpty(password) && ProvisioningToolOptions.UpdateUserSecrets) { CodeWriter.AddUserSecrets(applicationParameters.IsB2C, ProvisioningToolOptions.ProjectPath, password); @@ -190,7 +196,8 @@ await WriteApplicationRegistration( return effectiveApplicationParameters; } - // add 'MicrosoftGraph' or 'DownstreamAPI' section. + // add 'AzureAd', 'MicrosoftGraph' or 'DownstreamAPI' sections as appropriate. Fill them default values if empty. + // Default values can be found https://github.com/dotnet/aspnetcore/tree/main/src/ProjectTemplates/Web.ProjectTemplates/content private void ModifyAppSettings(ApplicationParameters applicationParameters) { string? filePath = ProvisioningToolOptions.AppSettingsFilePath; @@ -211,21 +218,21 @@ private void ModifyAppSettings(ApplicationParameters applicationParameters) !azureAdProperty.Domain.Equals(applicationParameters.Domain, StringComparison.OrdinalIgnoreCase)) { changesMade = true; - azureAdToken["Domain"] = applicationParameters.Domain; + azureAdToken["Domain"] = applicationParameters.Domain ?? AzureAdDefaultProperties.Domain; } if (!string.IsNullOrEmpty(azureAdProperty.TenantId) && !azureAdProperty.TenantId.Equals(applicationParameters.TenantId, StringComparison.OrdinalIgnoreCase)) { changesMade = true; - azureAdToken["TenantId"] = applicationParameters.TenantId; + azureAdToken["TenantId"] = applicationParameters.TenantId ?? AzureAdDefaultProperties.TenantId; } if (!string.IsNullOrEmpty(azureAdProperty.ClientId) && !azureAdProperty.ClientId.Equals(applicationParameters.ClientId, StringComparison.OrdinalIgnoreCase)) { changesMade = true; - azureAdToken["ClientId"] = applicationParameters.ClientId; + azureAdToken["ClientId"] = applicationParameters.ClientId ?? AzureAdDefaultProperties.ClientId; } if (!string.IsNullOrEmpty(azureAdProperty.Instance) && @@ -249,9 +256,9 @@ private void ModifyAppSettings(ApplicationParameters applicationParameters) appSettings.Add("AzureAd", JObject.FromObject(new { Instance = applicationParameters.Instance ?? AzureAdDefaultProperties.Instance, - Domain = applicationParameters.Domain, - TenantId = applicationParameters.TenantId, - ClientId = applicationParameters.ClientId, + Domain = applicationParameters.Domain ?? AzureAdDefaultProperties.Domain, + TenantId = applicationParameters.TenantId ?? AzureAdDefaultProperties.TenantId, + ClientId = applicationParameters.ClientId ?? AzureAdDefaultProperties.ClientId, CallbackPath = applicationParameters.CallbackPath ?? AzureAdDefaultProperties.CallbackPath })); } From dcdf3493e83ea8d85d1c3469514b78172abada55 Mon Sep 17 00:00:00 2001 From: Deep Choudhery Date: Mon, 12 Apr 2021 13:49:59 -0700 Subject: [PATCH 3/5] minor cleanup. added ReadApplication --- .../AzureAdProperties.cs | 4 ++-- .../Microsoft.DotNet.MsIdentity.csproj | 1 - ...osoftIdentityPlatformApplicationManager.cs | 2 +- .../Tool/AppProvisioningTool.cs | 22 ++++++++++++++++--- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs index e99eac279..db0be6f81 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/AuthenticationParameters/AzureAdProperties.cs @@ -18,8 +18,8 @@ public class AzureAdProperties public static class AzureAdDefaultProperties { public const string Domain = "qualified.domain.name"; - public const string ClientId = "22222222-2222-2222-2222-222222222222"; - public const string TenantId = "11111111-1111-1111-11111111111111111"; + public const string TenantId = "22222222-2222-2222-2222-222222222222"; + public const string ClientId = "11111111-1111-1111-11111111111111111"; public const string Instance = "https://login.microsoftonline.com/"; public const string CallbackPath = "/signin-oidc"; } diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj index 250c972b9..5849691c9 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj @@ -41,7 +41,6 @@ - diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs index 6cc58b5bc..a53f1642b 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/MicrosoftIdentityPlatform/MicrosoftIdentityPlatformApplicationManager.cs @@ -254,7 +254,7 @@ internal static async Task AddPasswordCredentials(GraphServiceClient graphServic { var passwordCredential = new PasswordCredential { - DisplayName = "Password created by dotnet-msidentity tool" + DisplayName = "Secret created by dotnet-msidentity tool" }; if (!string.IsNullOrEmpty(applicatonId) && graphServiceClient != null) diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs index 5fbd58631..53639a04d 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs @@ -84,7 +84,7 @@ public AppProvisioningTool(string commandName, ProvisioningToolOptions provision if (CommandName.Equals(Commands.UPDATE_PROJECT_COMMAND, StringComparison.OrdinalIgnoreCase)) { // Read or provision Microsoft identity platform application - ApplicationParameters? applicationParameters = await ReadOrProvisionMicrosoftIdentityApplication( + ApplicationParameters? applicationParameters = await ReadMicrosoftIdentityApplication( tokenCredential, projectSettings.ApplicationParameters); @@ -406,12 +406,28 @@ private bool Reconciliate(ApplicationParameters applicationParameters, Applicati return needUpdate; } - private async Task ReadOrProvisionMicrosoftIdentityApplication( + private async Task ReadMicrosoftIdentityApplication( TokenCredential tokenCredential, ApplicationParameters applicationParameters) { ApplicationParameters? currentApplicationParameters = null; - if (!string.IsNullOrEmpty(applicationParameters.EffectiveClientId) || !string.IsNullOrEmpty(applicationParameters.ClientId)) + if (!string.IsNullOrEmpty(applicationParameters.EffectiveClientId) || (!string.IsNullOrEmpty(applicationParameters.ClientId) && !AzureAdDefaultProperties.ClientId.Equals(applicationParameters.ClientId, StringComparison.OrdinalIgnoreCase))) + { + currentApplicationParameters = await MicrosoftIdentityPlatformApplicationManager.ReadApplication(tokenCredential, applicationParameters); + if (currentApplicationParameters == null) + { + Console.Write($"Couldn't find app {applicationParameters.EffectiveClientId} in tenant {applicationParameters.EffectiveTenantId}. "); + } + } + return currentApplicationParameters; + } + + private async Task ReadOrProvisionMicrosoftIdentityApplication( + TokenCredential tokenCredential, + ApplicationParameters applicationParameters) + { + ApplicationParameters? currentApplicationParameters = null; + if (!string.IsNullOrEmpty(applicationParameters.EffectiveClientId) || (!string.IsNullOrEmpty(applicationParameters.ClientId) && !AzureAdDefaultProperties.ClientId.Equals(applicationParameters.ClientId, StringComparison.OrdinalIgnoreCase))) { currentApplicationParameters = await MicrosoftIdentityPlatformApplicationManager.ReadApplication(tokenCredential, applicationParameters); if (currentApplicationParameters == null) From 8100c2b1553a852973d1f87a5675b61a803f8be8 Mon Sep 17 00:00:00 2001 From: Deep Choudhery Date: Mon, 12 Apr 2021 15:14:23 -0700 Subject: [PATCH 4/5] PR comments and minor fixes. --- eng/Versions.props | 3 ++- .../CodeReaderWriter/CodeWriter.cs | 2 ++ .../Microsoft.DotNet.MsIdentity.csproj | 2 +- .../Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs | 1 + src/Scaffolding/Shared/Cli.Utils/Command.cs | 5 +++++ src/Scaffolding/Shared/Cli.Utils/DotNetMuxer.cs | 3 +++ 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 80906cf65..396daeb7c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -46,7 +46,7 @@ 6.0.0-preview.3.21201.4 6.0.0-preview.3.21201.4 - 13.0.1 + 11.0.2 9.0.1 5.0.0 @@ -135,6 +135,7 @@ 2.18.0 4.7.2 2.0.0-beta1.20574.7 + 13.0.1 diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/CodeReaderWriter/CodeWriter.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/CodeReaderWriter/CodeWriter.cs index 4f945afcc..6f7dc461b 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/CodeReaderWriter/CodeWriter.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/CodeReaderWriter/CodeWriter.cs @@ -49,6 +49,7 @@ internal static void WriteConfiguration(Summary summary, IEnumerable(); diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj index 5849691c9..cdf48f7f2 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Microsoft.DotNet.MsIdentity.csproj @@ -32,7 +32,7 @@ - + diff --git a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs index 53639a04d..875726381 100644 --- a/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs +++ b/src/MsIdentityScaffolding/Microsoft.DotNet.MsIdentity/Tool/AppProvisioningTool.cs @@ -214,6 +214,7 @@ private void ModifyAppSettings(ApplicationParameters applicationParameters) var azureAdProperty = azureAdToken.ToObject(); if (azureAdProperty != null) { + // if property exists, and if suggested value is not already there. if (!string.IsNullOrEmpty(azureAdProperty.Domain) && !azureAdProperty.Domain.Equals(applicationParameters.Domain, StringComparison.OrdinalIgnoreCase)) { diff --git a/src/Scaffolding/Shared/Cli.Utils/Command.cs b/src/Scaffolding/Shared/Cli.Utils/Command.cs index 5bb2c93dd..d62934da9 100644 --- a/src/Scaffolding/Shared/Cli.Utils/Command.cs +++ b/src/Scaffolding/Shared/Cli.Utils/Command.cs @@ -26,7 +26,10 @@ internal static Command Create(string commandName, IEnumerable args) return new Command(commandName, args); } + //TODO; fix to have a nullable Command and not resure scaffolding's. Tracked https://github.com/dotnet/Scaffolding/issues/1549 +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. private Command(string commandName, IEnumerable args) +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. { var psi = new ProcessStartInfo { @@ -115,7 +118,9 @@ public Command OnErrorLine(Action handler) return this; } +#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. private void ThrowIfRunning([CallerMemberName] string memberName = null) +#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. { if (_running) { diff --git a/src/Scaffolding/Shared/Cli.Utils/DotNetMuxer.cs b/src/Scaffolding/Shared/Cli.Utils/DotNetMuxer.cs index b0bb13635..edbf9a6c5 100644 --- a/src/Scaffolding/Shared/Cli.Utils/DotNetMuxer.cs +++ b/src/Scaffolding/Shared/Cli.Utils/DotNetMuxer.cs @@ -37,7 +37,10 @@ private static string TryFindMuxerPath() return mainModule.FileName; } + //TODO; fix to have a nullable Command and not resure scaffolding's. Tracked https://github.com/dotnet/Scaffolding/issues/1549 +#pragma warning disable CS8603 // Possible null reference return. return null; +#pragma warning restore CS8603 // Possible null reference return. } } } From d1d9b8f6fe485eb5b6b5170044466e0500bb2e2f Mon Sep 17 00:00:00 2001 From: Deep Choudhery Date: Mon, 12 Apr 2021 15:33:31 -0700 Subject: [PATCH 5/5] update readme formsidentity --- src/MsIdentityScaffolding/README.md | 5 ++++- tools/dotnet-msidentity/README.md | 12 +++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/MsIdentityScaffolding/README.md b/src/MsIdentityScaffolding/README.md index f5ea6abd9..9760297ae 100644 --- a/src/MsIdentityScaffolding/README.md +++ b/src/MsIdentityScaffolding/README.md @@ -34,13 +34,16 @@ Usage: dotnet-msidentity [command] [options] Commands: - --register-application Registers/unregisters/updates an AAD/AAD B2C Application in Azure. + --register-app Registers/updates an AAD/AAD B2C Application in Azure. - Updates the appsettings.json file. + - Updates local user secrets + --unregister-app Unregister an AAD/AAD B2C Application in Azure. Internal Commands (These commands have little do with registering AAD/AAD B2C apps but are nice helpers): --list--aad-apps Lists AAD Applications for a given tenant + username. --list-service-principals Lists AAD Service Principals for a given tenant + username. --list-tenants Lists AAD + AAD B2C tenants for a given username. + --update-project Given client id for an Azure AD/AD B2C app, update appsettings.json, local user secrets. [TODO : and project code(Startup.cs, project references to get the app auth ready).] Options: --tenant-id Azure AD or Azure AD B2C tenant in which to create/update the app. diff --git a/tools/dotnet-msidentity/README.md b/tools/dotnet-msidentity/README.md index 4826aeee6..9760297ae 100644 --- a/tools/dotnet-msidentity/README.md +++ b/tools/dotnet-msidentity/README.md @@ -34,10 +34,16 @@ Usage: dotnet-msidentity [command] [options] Commands: - --list--aad-apps Lists AAD Applications for a given tenant/username. - --list-service-principals Lists AAD Service Principals. - --register-application Registers/unregisters/updates an AAD/AAD B2C Application in Azure. + --register-app Registers/updates an AAD/AAD B2C Application in Azure. - Updates the appsettings.json file. + - Updates local user secrets + --unregister-app Unregister an AAD/AAD B2C Application in Azure. + +Internal Commands (These commands have little do with registering AAD/AAD B2C apps but are nice helpers): + --list--aad-apps Lists AAD Applications for a given tenant + username. + --list-service-principals Lists AAD Service Principals for a given tenant + username. + --list-tenants Lists AAD + AAD B2C tenants for a given username. + --update-project Given client id for an Azure AD/AD B2C app, update appsettings.json, local user secrets. [TODO : and project code(Startup.cs, project references to get the app auth ready).] Options: --tenant-id Azure AD or Azure AD B2C tenant in which to create/update the app.