Skip to content

Commit

Permalink
dotnet-msidentity Preview 1 (#1548)
Browse files Browse the repository at this point in the history
* initial commit

* minor cleanup

* minor cleanup. added ReadApplication

* PR comments and minor fixes.

* update readme formsidentity
  • Loading branch information
deepchoudhery authored Apr 12, 2021
1 parent d4cd8e7 commit a26de0a
Show file tree
Hide file tree
Showing 15 changed files with 440 additions and 87 deletions.
7 changes: 0 additions & 7 deletions eng/Versions.MsIdentity.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@
<UsingToolXliff>false</UsingToolXliff>
<UsingToolNetFrameworkReferenceAssemblies>true</UsingToolNetFrameworkReferenceAssemblies>
</PropertyGroup>
<!-- Production Dependencies -->
<PropertyGroup>
<MicrosoftBuildFrameworkPackageVersion>$(MicrosoftBuildPackageVersion)</MicrosoftBuildFrameworkPackageVersion>
<MicrosoftBuildRuntimePackageVersion>$(MicrosoftBuildPackageVersion)</MicrosoftBuildRuntimePackageVersion>
<MicrosoftBuildUtilitiesCorePackageVersion>$(MicrosoftBuildPackageVersion)</MicrosoftBuildUtilitiesCorePackageVersion>
<!-- Ref packages -->
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
<PreReleaseVersionLabel>preview</PreReleaseVersionLabel>
Expand Down
1 change: 1 addition & 0 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
<MicrosoftIdentityClientExtensionsMsalVersion>2.18.0</MicrosoftIdentityClientExtensionsMsalVersion>
<SystemTextJsonVersion>4.7.2</SystemTextJsonVersion>
<SystemCommandLineVersion>2.0.0-beta1.20574.7</SystemCommandLineVersion>
<NewtonsoftJsonMsIdentityPackageVersion>13.0.1</NewtonsoftJsonMsIdentityPackageVersion>
</PropertyGroup>

</Project>
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 System;
Expand All @@ -16,6 +16,11 @@ public class ApplicationParameters
/// </summary>
public string? ApplicationDisplayName { get; set; }

/// <summary>
/// Application project path
/// <summary>
public string? ProjectPath { get; set; }

/// <summary>
/// Tenant in which the application is created.
/// </summary>
Expand Down Expand Up @@ -179,6 +184,11 @@ public string? Domain1
/// </summary>
public string? MsalAuthenticationOptions { get; set; }

/// <summary>
/// Graph.Application and Graph.ServicePrincipal object ids.
/// </summary>
public string? GraphEntityId { get; set; }

/// <summary>
/// Sets a bool property (from its name).
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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; }
}

//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";
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";
}
}
Original file line number Diff line number Diff line change
@@ -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<Replacement> replacements, ApplicationParameters reconcialedApplicationParameters)
internal static void WriteConfiguration(Summary summary, IEnumerable<Replacement> replacements, ApplicationParameters reconcialedApplicationParameters)
{
foreach (var replacementsInFile in replacements.GroupBy(r => r.FilePath))
{
Expand Down Expand Up @@ -49,36 +49,88 @@ internal void WriteConfiguration(Summary summary, IEnumerable<Replacement> repla
}
}

private string? ComputeReplacement(string replaceBy, ApplicationParameters reconciledApplicationParameters)
//TODO : Add integration tests for testing instead of mocking for unit tests.
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<string>();
var output = new List<string>();

IList<string> arguments = new List<string>();

//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<string>();
var output = new List<string>();

IList<string> arguments = new List<string>();

//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
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net5.0</TargetFrameworks>
Expand Down Expand Up @@ -32,8 +32,14 @@
<PackageReference Include="Microsoft.Graph" Version="$(MicrosoftGraphVersion)" />
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="$(MicrosoftIdentityClientExtensionsMsalVersion)" />
<PackageReference Include="System.Text.Json" Version="$(SystemTextJsonVersion)" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonMsIdentityPackageVersion)" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\..\Scaffolding\Shared\Cli.Utils\*.cs">
<Link>Shared\%(RecursiveDir)%(FileName).cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// 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.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Azure.Core;
using Microsoft.DotNet.MsIdentity.AuthenticationParameters;
using Microsoft.Graph;

namespace Microsoft.DotNet.MsIdentity.MicrosoftIdentityPlatformApplication
{
Expand Down Expand Up @@ -123,7 +122,7 @@ await AddApiPermissionFromBlazorwasmHostedSpaToServerApi(
{
await AddPasswordCredentials(
graphServiceClient,
createdApplication,
createdApplication.Id,
effectiveApplicationParameters);
}

Expand Down Expand Up @@ -193,7 +192,7 @@ await graphServiceClient.Applications[existingApplication.Id]
{
await AddPasswordCredentials(
graphServiceClient,
existingApplication,
existingApplication.Id,
reconcialedApplicationParameters).ConfigureAwait(false);
}
}
Expand Down Expand Up @@ -251,18 +250,21 @@ await graphServiceClient.Oauth2PermissionGrants
/// <param name="createdApplication"></param>
/// <param name="effectiveApplicationParameters"></param>
/// <returns></returns>
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 = "Secret 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)
{
PasswordCredential returnedPasswordCredential = await graphServiceClient.Applications[$"{applicatonId}"]
.AddPassword(passwordCredential)
.Request()
.PostAsync();
effectiveApplicationParameters.PasswordCredentials.Add(returnedPasswordCredential.SecretText);
}
}

/// <summary>
Expand Down Expand Up @@ -539,7 +541,7 @@ await graphServiceClient.Applications[$"{readApplication.Id}"]
}
}

private GraphServiceClient GetGraphServiceClient(TokenCredential tokenCredential)
internal GraphServiceClient GetGraphServiceClient(TokenCredential tokenCredential)
{
if (_graphServiceClient == null)
{
Expand All @@ -551,10 +553,25 @@ private GraphServiceClient GetGraphServiceClient(TokenCredential tokenCredential
public async Task<ApplicationParameters?> 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<Application?> GetApplication(TokenCredential tokenCredential, ApplicationParameters applicationParameters)
{
var graphServiceClient = GetGraphServiceClient(tokenCredential);
var apps = await graphServiceClient.Applications
.Request()
.Filter($"appId eq '{applicationParameters.ClientId}'")
Expand All @@ -566,14 +583,7 @@ private GraphServiceClient GetGraphServiceClient(TokenCredential tokenCredential
{
return null;
}

ApplicationParameters effectiveApplicationParameters = GetEffectiveApplicationParameters(
tenant!,
readApplication,
applicationParameters);

return effectiveApplicationParameters;

return readApplication;
}

private ApplicationParameters GetEffectiveApplicationParameters(
Expand Down Expand Up @@ -607,7 +617,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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ public class ProjectDescriptionReader
{
files = new string[0];
}

foreach (string filePath in files)
{
// If there are matches, one at least needs to match
Expand Down
Loading

0 comments on commit a26de0a

Please sign in to comment.