Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CIAM updates (#2422) #2432

Merged
merged 2 commits into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion eng/Versions.MSIdentity.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<UsingToolNetFrameworkReferenceAssemblies>true</UsingToolNetFrameworkReferenceAssemblies>
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>2.0.3</VersionPrefix>
<VersionPrefix>2.0.4</VersionPrefix>
<PreReleaseVersionLabel>rtm</PreReleaseVersionLabel>
<IsServicingBuild Condition="'$(PreReleaseVersionLabel)' == 'servicing'">true</IsServicingBuild>
<!--
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ public string? Domain1
/// </summary>
public bool IsB2C { get; set; }

/// <summary>
/// Is authenticated with CIAM.
/// </summary>
public bool IsCiam { get; set; }

/// <summary>
/// Sign-up/sign-in policy in the case of B2C.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public static class PropertyNames
// https://github.com/dotnet/aspnetcore/blob/6bc4b79f4ee7af00edcbb435e5ee4c1de349a110/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/appsettings.json
public static class DefaultProperties
{
public const string Authority = "https://qualified.domain.name/";
public const string Domain = "qualified.domain.name";
public const string TenantId = "22222222-2222-2222-2222-222222222222";
public const string ClientId = "11111111-1111-1111-11111111111111111";
Expand All @@ -53,6 +54,7 @@ public class AzureAdBlock
public bool IsBlazorWasm;
public bool IsWebApi;
public bool IsB2C;
public bool IsCIAM;
public bool HasClientSecret;

public string? ClientId;
Expand All @@ -76,18 +78,23 @@ public AzureAdBlock(ApplicationParameters applicationParameters, JObject? existi
IsBlazorWasm = applicationParameters.IsBlazorWasm;
IsWebApi = applicationParameters.IsWebApi.GetValueOrDefault();
IsB2C = applicationParameters.IsB2C;
IsCIAM = applicationParameters.IsCiam;
HasClientSecret = applicationParameters.CallsDownstreamApi || applicationParameters.CallsMicrosoftGraph;

Domain = !string.IsNullOrEmpty(applicationParameters.Domain) ? applicationParameters.Domain : existingBlock?.GetValue(PropertyNames.Domain)?.ToString() ?? DefaultProperties.Domain;
if (IsCIAM)
{
Domain = Domain.Replace("onmicrosoft.com", "ciamlogin.com");
}

TenantId = !string.IsNullOrEmpty(applicationParameters.TenantId) ? applicationParameters.TenantId : existingBlock?.GetValue(PropertyNames.TenantId)?.ToString() ?? DefaultProperties.TenantId;
ClientId = !string.IsNullOrEmpty(applicationParameters.ClientId) ? applicationParameters.ClientId : existingBlock?.GetValue(PropertyNames.ClientId)?.ToString() ?? DefaultProperties.ClientId;
Instance = !string.IsNullOrEmpty(applicationParameters.Instance) ? applicationParameters.Instance : existingBlock?.GetValue(PropertyNames.Instance)?.ToString() ?? DefaultProperties.Instance;
CallbackPath = !string.IsNullOrEmpty(applicationParameters.CallbackPath) ? applicationParameters.CallbackPath : existingBlock?.GetValue(PropertyNames.CallbackPath)?.ToString() ?? DefaultProperties.CallbackPath;
Scopes = !string.IsNullOrEmpty(applicationParameters.CalledApiScopes) ? applicationParameters.CalledApiScopes : existingBlock?.GetValue(PropertyNames.Scopes)?.ToString()
?? (applicationParameters.CallsDownstreamApi ? DefaultProperties.ApiScopes : applicationParameters.CallsMicrosoftGraph ? DefaultProperties.MicrosoftGraphScopes : null);
SignUpSignInPolicyId = !string.IsNullOrEmpty(applicationParameters.SusiPolicy) ? applicationParameters.SusiPolicy : existingBlock?.GetValue(PropertyNames.SignUpSignInPolicyId)?.ToString() ?? DefaultProperties.SignUpSignInPolicyId;
// TODO determine the SusiPolicy from the graph beta
Authority = IsB2C ? $"{Instance}{Domain}/{SignUpSignInPolicyId}" : $"{Instance}{Domain}";
Authority = IsCIAM ? $"https://{Domain}/" : IsB2C ? $"{Instance}{Domain}/{SignUpSignInPolicyId}" : $"{Instance}{Domain}";
ClientSecret = existingBlock?.GetValue(PropertyNames.ClientSecret)?.ToString() ?? DefaultProperties.ClientSecret;
ClientCertificates = existingBlock?.GetValue(PropertyNames.ClientCertificates)?.ToObject<string[]>();
}
Expand All @@ -99,6 +106,15 @@ public AzureAdBlock(ApplicationParameters applicationParameters, JObject? existi
ValidateAuthority = !IsB2C
};

public dynamic CIAMSettings => new
{
Authority = Authority ?? DefaultProperties.Authority,
ClientId = ClientId ?? DefaultProperties.ClientId,
ClientSecret = ClientSecret ?? DefaultProperties.ClientSecret,
ClientCertificates = ClientCertificates ?? Array.Empty<string>(),
CallbackPath = CallbackPath ?? DefaultProperties.CallbackPath
};

public dynamic WebAppSettings => new
{
Instance = Instance ?? DefaultProperties.Instance,
Expand Down Expand Up @@ -140,6 +156,11 @@ public JObject ToJObject()
return JObject.FromObject(BlazorSettings);
}

if (IsCIAM)
{
return JObject.FromObject(CIAMSettings);
}

var jObject = IsWebApi ? JObject.FromObject(WebApiSettings) : JObject.FromObject(WebAppSettings);

if (IsB2C)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@
]
},
{
"FileName": "LoginDisplay.razor",
"FileName": "blazorserver_LoginDisplay.razor",
"AddFilePath": "Shared/LoginDisplay.razor"
},
{
Expand All @@ -268,4 +268,4 @@
]
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@
"AddFilePath": "Pages/Authentication.razor"
},
{
"FileName": "LoginDisplay.razor",
"FileName": "blazorwasm_LoginDisplay.razor",
"AddFilePath": "Shared/LoginDisplay.razor"
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,25 +140,23 @@
"LeadingTrivia": {
"Newline": true
}
},
},
{
"CodeChangeType": "Lambda",
"Parent": "WebApplication.CreateBuilder.Services.AddAuthorization",
"Block": "options.FallbackPolicy = options.DefaultPolicy",
"Parameter": "options",
"LeadingTrivia":
{
"Newline":true,
"LeadingTrivia": {
"Newline": true,
"NumberOfSpaces": 4
}
},
{
"Parent": "WebApplication.CreateBuilder.Services.AddRazorPages",
"CodeChangeType": "MemberAccess",
"Block": "AddMicrosoftIdentityUI()",
"LeadingTrivia":
{
"Newline":true,
"LeadingTrivia": {
"Newline": true,
"NumberOfSpaces": 4
}
},
Expand Down Expand Up @@ -187,10 +185,10 @@
},
{
"FileName": "Index.cshtml.cs",
"Options" : [ "MicrosoftGraph", "DownstreamApi" ],
"Options": [ "MicrosoftGraph", "DownstreamApi" ],
"ClassProperties": [
{
"Block" : "private readonly GraphServiceClient _graphServiceClient",
"Block": "private readonly GraphServiceClient _graphServiceClient",
"Options": [ "MicrosoftGraph" ]
},
{
Expand All @@ -209,50 +207,49 @@
}
],
"Methods": {
"OnGet":
{
"EditType" : {
"Block": "async Task",
"Options": ["MicrosoftGraph", "DownstreamApi"]
"OnGet": {
"EditType": {
"Block": "async Task",
"Options": [ "MicrosoftGraph", "DownstreamApi" ]
},
"CodeChanges": [
{
"Block": "var user = await _graphServiceClient.Me.Request().GetAsync();",
"LeadingTrivia": {
"NumberOfSpaces": 12
},
"Options" : [ "MicrosoftGraph"]
"Options": [ "MicrosoftGraph" ]
},
{
"Block": "ViewData[\"GraphApiResult\"] = user.DisplayName;",
"LeadingTrivia": {
"NumberOfSpaces": 12
},
"Options" : [ "MicrosoftGraph"]
"Options": [ "MicrosoftGraph" ]
},
{
"Block" : "using var response = await _downstreamWebApi.CallWebApiForUserAsync(\"DownstreamApi\").ConfigureAwait(false);",
"Block": "using var response = await _downstreamWebApi.CallWebApiForUserAsync(\"DownstreamApi\").ConfigureAwait(false);",
"LeadingTrivia": {
"NumberOfSpaces": 12
},
"Options" : [ "DownstreamApi"]
"Options": [ "DownstreamApi" ]
},
{
"Block": "\n\n if (response.StatusCode == System.Net.HttpStatusCode.OK)\n {\n var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false);\n ViewData[\"ApiResult\"] = apiResult;\n }\n else\n {\n var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false);\n throw new HttpRequestException($\"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}\");\n }",
"Options": [ "DownstreamApi" ]
}
]
},
"IndexModel" : {
"Parameters" : [ "ILogger<IndexModel>" ],
"AddParameters" : [
"IndexModel": {
"Parameters": [ "ILogger<IndexModel>" ],
"AddParameters": [
{
"Block":"GraphServiceClient graphServiceClient",
"Options": [ "MicrosoftGraph"]
"Block": "GraphServiceClient graphServiceClient",
"Options": [ "MicrosoftGraph" ]
},
{
"Block":"IDownstreamWebApi downstreamWebApi",
"Options": [ "DownstreamApi"]
"Block": "IDownstreamWebApi downstreamWebApi",
"Options": [ "DownstreamApi" ]
}
],
"CodeChanges": [
Expand All @@ -273,7 +270,7 @@
]
}
},
"Usings" : [
"Usings": [
"Microsoft.Identity.Web",
"System.Net"
],
Expand Down Expand Up @@ -301,6 +298,30 @@
]
}
}
},
{
"FileName": "_Layout.cshtml",
"Methods": {
"Global": {
"CodeChanges": [
{
"MultiLineBlock": [
"</ul>",
" <partial name=\"_LoginPartial\" />",
" </div>"
],
"ReplaceSnippet": [
"</ul>",
" </div>"
]
}
]
}
}
},
{
"FileName": "LoginPartial.cshtml",
"AddFilePath": "Pages/Shared/_LoginPartial.cshtml"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,26 +88,21 @@ public async Task AddAuthCodeAsync()
var filteredFiles = codeModifierConfig.Files.Where(f => ProjectModifierHelper.FilterOptions(f.Options, options));
foreach (var file in filteredFiles)
{
await HandleCodeFileAsync(file, project, options, codeModifierConfig.Identifier);
await HandleCodeFileAsync(file, project, options);
}

_consoleLogger.LogJsonMessage(State.Success, output: _output.ToString().TrimEnd());
}

internal static string GetCodeFileString(CodeFile file, string identifier) // todo make all code files strings
internal static string GetCodeFileString(CodeFile file)
{
// Resource files cannot contain '-' (dash) or '.' (period)
var codeFilePropertyName = $"add_{identifier.Replace('-', '_')}_{file.FileName.Replace('.', '_')}";
var codeFilePropertyName = $"add_{file.FileName.Replace('.', '_')}";
var property = AppProvisioningTool.Properties.FirstOrDefault(
p => p.Name.Equals(codeFilePropertyName));

if (property is null)
{
throw new FormatException($"Resource property for {file.FileName} could not be found. ");
}
p => p.Name.Equals(codeFilePropertyName))
?? throw new FormatException($"Resource property for {file.FileName} could not be found. ");

var codeFileString = property.GetValue(typeof(Resources))?.ToString();

if (string.IsNullOrEmpty(codeFileString))
{
throw new FormatException($"CodeFile string for {file.FileName} was empty.");
Expand All @@ -116,7 +111,7 @@ internal static string GetCodeFileString(CodeFile file, string identifier) // to
return codeFileString;
}

internal static ClassDeclarationSyntax ModifyMethods(string fileName, ClassDeclarationSyntax classNode, DocumentBuilder documentBuilder, Dictionary<string, Method> methods, CodeChangeOptions options, StringBuilder output)
internal static ClassDeclarationSyntax ModifyMethods(string fileName, ClassDeclarationSyntax classNode, Dictionary<string, Method> methods, CodeChangeOptions options, StringBuilder output)
{
foreach ((string methodName, Method methodChanges) in methods)
{
Expand Down Expand Up @@ -283,13 +278,13 @@ private PropertyInfo? CodeModifierConfigPropertyInfo
}
}

private async Task HandleCodeFileAsync(CodeFile file, CodeAnalysis.Project project, CodeChangeOptions options, string identifier)
private async Task HandleCodeFileAsync(CodeFile file, CodeAnalysis.Project project, CodeChangeOptions options)
{
try
{
if (!string.IsNullOrEmpty(file.AddFilePath))
{
AddFile(file, identifier);
AddFile(file);
_output.AppendLine(string.Format(Resources.AddedCodeFile, file.AddFilePath));
}
else
Expand Down Expand Up @@ -325,15 +320,15 @@ private async Task HandleCodeFileAsync(CodeFile file, CodeAnalysis.Project proje
/// <param name="file"></param>
/// <param name="identifier"></param>
/// <exception cref="FormatException"></exception>
private void AddFile(CodeFile file, string identifier)
private void AddFile(CodeFile file)
{
var filePath = Path.Combine(_toolOptions.ProjectPath, file.AddFilePath);
if (File.Exists(filePath))
{
return; // File exists, don't need to create
}

var codeFileString = GetCodeFileString(file, identifier);
var codeFileString = GetCodeFileString(file);

var fileDir = Path.GetDirectoryName(filePath);
if (!string.IsNullOrEmpty(fileDir))
Expand Down Expand Up @@ -396,7 +391,7 @@ node is ClassDeclarationSyntax cds &&
//add class attributes
modifiedClassDeclarationSyntax = documentBuilder.AddClassAttributes(modifiedClassDeclarationSyntax, options);
//add code snippets/changes.
modifiedClassDeclarationSyntax = ModifyMethods(file.FileName, modifiedClassDeclarationSyntax, documentBuilder, file.Methods, options, _output);
modifiedClassDeclarationSyntax = ModifyMethods(file.FileName, modifiedClassDeclarationSyntax, file.Methods, options, _output);

//replace class node with all the updates.
#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type.
Expand Down
Loading