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

Blazor Wasm: Code Updates and AppSettings updates #1805

Merged
merged 10 commits into from
Jan 27, 2022
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>1.0.1</VersionPrefix>
<VersionPrefix>1.0.2</VersionPrefix>
<PreReleaseVersionLabel>rtm</PreReleaseVersionLabel>
<IsServicingBuild Condition="'$(PreReleaseVersionLabel)' == 'servicing'">true</IsServicingBuild>
<!--
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public string? Domain1
/// <summary>
/// The project is a blazor web assembly.
/// </summary>
public bool? IsBlazorWasm { get; set; }
public bool IsBlazorWasm { get; set; }

/// <summary>
/// The app calls Microsoft Graph.
Expand Down Expand Up @@ -155,7 +155,7 @@ public string? Domain1
public List<string> PasswordCredentials { get; } = new List<string>();

/// <summary>
/// Identitier URIs for web APIs.
/// Identifier URIs for web APIs.
/// </summary>
public string? AppIdUri { set; get; }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.DotNet.MSIdentity.AuthenticationParameters
{
public class AzureAdProperties
public class PropertyNames
{
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 const string Domain = nameof(Domain);
public const string TenantId = nameof(TenantId);
public const string ClientId = nameof(ClientId);
public const string ClientSecret = nameof(ClientSecret);
public const string ClientCertificates = nameof(ClientCertificates);
public const string CallbackPath = nameof(CallbackPath);
public const string Instance = nameof(Instance);

public const string Authority = nameof(Authority);
public const string ValidateAuthority = nameof(ValidateAuthority);

public const string BaseUrl = nameof(BaseUrl);
public const string Scopes = nameof(Scopes);
}

//getting default properties from https://github.com/dotnet/aspnetcore/blob/6bc4b79f4ee7af00edcbb435e5ee4c1de349a110/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/appsettings.json
public static class AzureAdDefaultProperties
// getting default properties from
// https://github.com/dotnet/aspnetcore/blob/6bc4b79f4ee7af00edcbb435e5ee4c1de349a110/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/appsettings.json
public static class DefaultProperties
{
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";
public const string ClientSecret = "Client secret from app-registration. Check user secrets/azure portal.";

public const string Authority = "https://login.microsoftonline.com/22222222-2222-2222-2222-222222222222";
public const string ValidateAuthority = "true";

public const string MicrosoftGraphBaseUrl = "https://graph.microsoft.com/v1.0";
public const string MicrosoftGraphScopes = "user.read";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,60 +24,21 @@ else
<td>Name</td>
<td>@user.DisplayName</td>
</tr>
<tr>
<td>Photo</td>
<td>
@{
if (photo != null)
{
<img style="margin: 5px 0; width: 150px" src="data:image/jpeg;base64, @photo" />
}
else
{
<h3>NO PHOTO</h3>
<p>Check user profile in Azure Active Directory to add a photo.</p>
}
}
</td>
</tr>
</table>
}

@code {
User user;
string photo;
User? user;

protected override async Task OnInitializedAsync()
{
try
{
user = await GraphServiceClient.Me.Request().GetAsync();
photo = await GetPhoto();
}
catch (Exception ex)
{
ConsentHandler.HandleException(ex);
}
}

protected async Task<string> GetPhoto()
{
string photo;

try
{
using (var photoStream = await GraphServiceClient.Me.Photo.Content.Request().GetAsync())
{
byte[] photoByte = ((System.IO.MemoryStream)photoStream).ToArray();
photo = Convert.ToBase64String(photoByte);
this.StateHasChanged();
}

}
catch (Exception)
{
photo = null;
}
return photo;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@page "/authentication/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="@Action" />

@code{
[Parameter] public string? Action { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

@inject NavigationManager Navigation
@inject SignOutSessionStateManager SignOutManager

<AuthorizeView>
<Authorized>
Hello, @context.User.Identity?.Name!
<button class="nav-link btn btn-link" @onclick="BeginLogout">Log out</button>
</Authorized>
<NotAuthorized>
<a href="authentication/login">Log in</a>
</NotAuthorized>
</AuthorizeView>

@code{
private async Task BeginLogout(MouseEventArgs args)
{
await SignOutManager.SetSignOutState();
Navigation.NavigateTo("authentication/logout");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@inject NavigationManager Navigation

@code {
protected override void OnInitialized()
{
Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
},
{
"FileName": "App.razor",
"RazorChanges": [
"Replacements": [
{
"Block": "<CascadingAuthenticationState>\r\n <Router AppAssembly=\"@typeof(App).Assembly\">",
"ReplaceSnippet": "<Router AppAssembly=\"@typeof(App).Assembly\">"
Expand All @@ -192,7 +192,7 @@
"MicrosoftGraph",
"DownstreamApi"
],
"RazorChanges": [
"Replacements": [
{
"Block": "</NavLink>\r\n </div>\r\n <div class=\"nav-item px-3\">\r\n <NavLink class=\"nav-link\" href=\"showprofile\">\r\n <span class=\"oi oi-list-rich\" aria-hidden=\"true\"></span> Show profile\r\n </NavLink>\r\n </div>\r\n </nav>\r\n</div>",
"ReplaceSnippet": "</NavLink>\r\n </div>\r\n </nav>\r\n</div>",
Expand All @@ -211,7 +211,7 @@
},
{
"FileName": "MainLayout.razor",
"RazorChanges": [
"Replacements": [
{
"Block": " <main>\r\n <div class=\"top-row px-4 auth\">\r\n <LoginDisplay />",
"ReplaceSnippet": " <main>\r\n <div class=\"top-row px-4\">"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,84 @@
{

"Identifier": "dotnet-blazorwasm",
"Files": [
{
"FileName": "Program.cs",
"Options": [ "MinimalApp" ],
"Methods": {
"Global": {
"CodeChanges": [
{
"Block": "builder.Services.AddMsalAuthentication(options =>\r\n{\r\n builder.Configuration.Bind(\"AzureAd\", options.ProviderOptions.Authentication);\r\n});\r\n",
"InsertAfter": "builder.Services.AddScoped",
"InsertBefore": [
"builder.Build"
],
"CodeFormatting": {
"Newline": true
}
}
]
}
}
},
{
"FileName": "_Imports.razor",
"Replacements": [
{
"Block": "@using Microsoft.AspNetCore.Components.Authorization\r\n"
}
]
},
{
"FileName": "App.razor",
"Replacements": [
{
"Block": "<CascadingAuthenticationState>\r\n <Router AppAssembly=\"@typeof(App).Assembly\">",
"ReplaceSnippet": "<Router AppAssembly=\"@typeof(App).Assembly\">"
},
{
"Block": " <Found Context=\"routeData\">\r\n <AuthorizeRouteView RouteData=\"@routeData\" DefaultLayout=\"@typeof(MainLayout)\">\r\n <NotAuthorized>\r\n @if (context.User.Identity?.IsAuthenticated != true)\r\n {\r\n <RedirectToLogin />\r\n }\r\n else\r\n {\r\n <p role=\"alert\">You are not authorized to access this resource.</p>\r\n }\r\n </NotAuthorized>\r\n </AuthorizeRouteView>\r\n <FocusOnNavigate RouteData=\"@routeData\" Selector=\"h1\" />\r\n </Found>\r\n",
"ReplaceSnippet": " <Found Context=\"routeData\">\r\n <RouteView RouteData=\"@routeData\" DefaultLayout=\"@typeof(MainLayout)\" />\r\n <FocusOnNavigate RouteData=\"@routeData\" Selector=\"h1\" />\r\n </Found>\r\n"
},
{
"Block": " <NotFound>\r\n <PageTitle>Not found</PageTitle>\r\n <LayoutView Layout=\"@typeof(MainLayout)\">\r\n <p role=\"alert\">Sorry, there's nothing at this address.</p>\r\n </LayoutView>\r\n </NotFound>",
"ReplaceSnippet": " <NotFound>\r\n <PageTitle>Not found</PageTitle>\r\n <LayoutView Layout=\"@typeof(MainLayout)\">\r\n <p role=\"alert\">Sorry, there's nothing at this address.</p>\r\n </LayoutView>\r\n </NotFound>"
},
{
"Block": " </Router>\r\n</CascadingAuthenticationState>",
"ReplaceSnippet": "</Router>"
}
]
},
{
"FileName": "index.html",
"Replacements": [
{
"Block": " <script src=\"_content/Microsoft.Authentication.WebAssembly.Msal/AuthenticationService.js\"></script> \r\n <script src=\"_framework/blazor.webassembly.js\"></script>",
"ReplaceSnippet": " <script src=\"_framework/blazor.webassembly.js\"></script>"
}
]
},
{
"FileName": "Authentication.razor",
"AddFilePath": "Pages/Authentication.razor"
},
{
"FileName": "LoginDisplay.razor",
"AddFilePath": "Shared/LoginDisplay.razor"
},
{
"FileName": "MainLayout.razor",
"Replacements": [
{
"Block": " <div class=\"top-row px-4 auth\">\r\n <LoginDisplay />\r\n",
"ReplaceSnippet": " <div class=\"top-row px-4\">\r\n"
}
]
},
{
"FileName": "RedirectToLogin.razor",
"AddFilePath": "Shared/RedirectToLogin.razor"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,30 @@ public class CodeReader
/// <param name="projectDescriptions"></param>
/// <returns></returns>
public ProjectAuthenticationSettings ReadFromFiles(
string folderToConfigure,
ProjectDescription projectDescription,
IEnumerable<ProjectDescription> projectDescriptions)
IEnumerable<ProjectDescription> projectDescriptions,
IEnumerable<string> files)
{
ProjectAuthenticationSettings projectAuthenticationSettings = new ProjectAuthenticationSettings(projectDescription);
ProcessProject(
folderToConfigure,
projectDescription,
projectAuthenticationSettings,
projectDescriptions);
projectDescriptions,
files);
return projectAuthenticationSettings;
}

private static void ProcessProject(
string folderToConfigure,
private void ProcessProject(
ProjectDescription projectDescription,
ProjectAuthenticationSettings projectAuthenticationSettings,
IEnumerable<ProjectDescription> projectDescriptions)
IEnumerable<ProjectDescription> projectDescriptions,
IEnumerable<string> files)
{
string projectPath = Path.Combine(folderToConfigure, projectDescription.ProjectRelativeFolder!);

// TO-DO get all the project descriptions
var properties = projectDescription.GetMergedConfigurationProperties(projectDescriptions).ToArray();
var properties = projectDescription.GetMergedConfigurationProperties(projectDescriptions);
foreach (ConfigurationProperties configurationProperties in properties)
{
string? filePath = Directory.EnumerateFiles(projectPath, configurationProperties.FileRelativePath!).FirstOrDefault();
string? filePath = files.Where(f => f.Contains(configurationProperties.FileRelativePath!)).FirstOrDefault();
ProcessFile(projectAuthenticationSettings, filePath, configurationProperties);
}

Expand All @@ -72,12 +70,9 @@ private static void ProcessProject(
PostProcessWebUris(projectAuthenticationSettings);
}

private static void PostProcessWebUris(ProjectAuthenticationSettings projectAuthenticationSettings)
private void PostProcessWebUris(ProjectAuthenticationSettings projectAuthenticationSettings)
{
bool isBlazorWasm = projectAuthenticationSettings.ApplicationParameters.IsBlazorWasm.HasValue &&
projectAuthenticationSettings.ApplicationParameters.IsBlazorWasm.Value &&
projectAuthenticationSettings.ApplicationParameters.IsWebApp.HasValue &&
!projectAuthenticationSettings.ApplicationParameters.IsWebApp.Value;
bool isBlazorWasm = projectAuthenticationSettings.ApplicationParameters.IsBlazorWasm;
string callbackPath = projectAuthenticationSettings.ApplicationParameters.CallbackPath ?? "/signin-oidc";
if (isBlazorWasm)
{
Expand Down Expand Up @@ -187,8 +182,8 @@ private static void ProcessFile(
JsonElement element = pair.Key;
int index = pair.Value;
found = true;
string replaceFrom = element.ValueKind == JsonValueKind.Number
? element.GetInt32().ToString(CultureInfo.InvariantCulture)
string replaceFrom = element.ValueKind == JsonValueKind.Number
? element.GetInt32().ToString(CultureInfo.InvariantCulture)
: element.ToString()!;

UpdatePropertyRepresents(
Expand Down Expand Up @@ -338,14 +333,15 @@ private static void ReadCodeSetting(
if (!string.IsNullOrEmpty(value))
{
// TODO: something more generic
Uri authority = new Uri(value);
string? tenantOrDomain = authority.LocalPath.Split('/', StringSplitOptions.RemoveEmptyEntries)[0];
if (tenantOrDomain == "qualified.domain.name")
if (Uri.TryCreate(value, UriKind.Absolute, out Uri? authority))
{
tenantOrDomain = null;
var tenantOrDomain = authority.LocalPath.Split('/', StringSplitOptions.RemoveEmptyEntries)[0];
if (tenantOrDomain != "qualified.domain.name")
{
projectAuthenticationSettings.ApplicationParameters.Domain = tenantOrDomain;
projectAuthenticationSettings.ApplicationParameters.TenantId = tenantOrDomain;
}
}
projectAuthenticationSettings.ApplicationParameters.Domain = tenantOrDomain;
projectAuthenticationSettings.ApplicationParameters.TenantId = tenantOrDomain;
}
break;
case "Directory.Domain":
Expand Down
Loading