Skip to content

Commit 22e558b

Browse files
authored
Update formatting and integrate previous solution changes (#10)
* Bringover some of the changes from the previous solution * Apply the new formatting again * Remove unused package reference and add solution-level properties
1 parent f1809b8 commit 22e558b

File tree

217 files changed

+4370
-1161
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

217 files changed

+4370
-1161
lines changed

AStar.Web.slnx

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,36 @@
11
<Solution>
2-
<Folder Name="/Solution Items/" />
2+
<Folder Name="/Solution Items/">
3+
<File Path="Directory.Build.props"/>
4+
</Folder>
35
<Folder Name="/Solution Items/.github/">
4-
<File Path=".github\codeql.yml" />
5-
<File Path=".github\copilot-instructions.md" />
6-
<File Path=".github\dependabot.yml" />
6+
<File Path=".github\codeql.yml"/>
7+
<File Path=".github\copilot-instructions.md"/>
8+
<File Path=".github\dependabot.yml"/>
79
</Folder>
810
<Folder Name="/Solution Items/.github/instructions/">
9-
<File Path=".github\instructions\blazor.instructions.md" />
11+
<File Path=".github\instructions\blazor.instructions.md"/>
1012
</Folder>
1113
<Folder Name="/Solution Items/.github/prompts/">
12-
<File Path=".github\prompts\astar.prompt.md" />
13-
<File Path=".github\prompts\xunit.prompt.md" />
14+
<File Path=".github\prompts\astar.prompt.md"/>
15+
<File Path=".github\prompts\xunit.prompt.md"/>
1416
</Folder>
1517
<Folder Name="/Solution Items/.github/workflows/">
16-
<File Path=".github\workflows\main_astar-dev.yml" />
18+
<File Path=".github\workflows\main_astar-dev.yml"/>
1719
</Folder>
18-
<Folder Name="/src/" />
20+
<Folder Name="/src/"/>
1921
<Folder Name="/src/aspire/">
20-
<Project Path="src/aspire/AStar.Web.AppHost/AStar.Web.AppHost.csproj" />
21-
<Project Path="src/aspire/AStar.Web.ServiceDefaults/AStar.Web.ServiceDefaults.csproj" />
22+
<Project Path="src/aspire/AStar.Web.AppHost/AStar.Web.AppHost.csproj"/>
23+
<Project Path="src/aspire/AStar.Web.ServiceDefaults/AStar.Web.ServiceDefaults.csproj"/>
2224
</Folder>
23-
<Folder Name="/src/modules/" />
25+
<Folder Name="/src/modules/"/>
2426
<Folder Name="/src/modules/apis/">
25-
<Project Path="src/modules/apis/AStar.Web.ApiService/AStar.Web.ApiService.csproj" />
27+
<Project Path="src/modules/apis/AStar.Web.ApiService/AStar.Web.ApiService.csproj"/>
2628
</Folder>
2729
<Folder Name="/src/uis/">
28-
<Project Path="src\uis\AStar.Dev.Web\AStar.Dev.Web.csproj" />
30+
<Project Path="src\uis\AStar.Dev.Web\AStar.Dev.Web.csproj"/>
2931
</Folder>
30-
<Folder Name="/test/" />
32+
<Folder Name="/test/"/>
3133
<Folder Name="/test/aspire/">
32-
<Project Path="test/aspire/AStar.Web.Tests/AStar.Web.Tests.csproj" />
34+
<Project Path="test/aspire/AStar.Web.Tests/AStar.Web.Tests.csproj"/>
3335
</Folder>
3436
</Solution>

Directory.Build.props

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project>
2+
<PropertyGroup>
3+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>true</ImplicitUsings>
7+
<InterceptorsNamespaces>
8+
$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated
9+
</InterceptorsNamespaces>
10+
<Features>$(Features);InterceptorsPreview</Features>
11+
</PropertyGroup>
12+
13+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
14+
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
15+
<NoWarn>1701;1702;</NoWarn>
16+
</PropertyGroup>
17+
18+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
19+
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
20+
<NoWarn>1701;1702;</NoWarn>
21+
</PropertyGroup>
22+
</Project>

blazor auth notes.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
✅ Blazor + Entra ID App Role Access Control Checklist
2+
3+
🔧 App Registration Setup
4+
5+
- App role defined in the App Registration manifest (e.g., "Admin").
6+
- App Registration is treated as an API:
7+
- Application ID URI is set (e.g., api://{client-id}).
8+
- At least one scope is defined under “Expose an API”.
9+
- accessTokenAcceptedVersion is set to 2 in the manifest.
10+
11+
12+
13+
👥 Role Assignment
14+
15+
- Users or groups are assigned to the app role via Enterprise Applications → Users and groups.
16+
- Role assignment is done on the service principal of the API App Registration.
17+
18+
🔐 Token Request Configuration
19+
20+
- Blazor app requests token for the API scope:
21+
22+
- Example: api://{API-App-ClientId}/.default
23+
24+
25+
- MSAL or Microsoft Identity Web is configured to request scopes correctly.
26+
27+
🧾 Token Validation
28+
29+
- Token contains the roles claim:
30+
31+
- Use https://jwt.ms to inspect the token.
32+
- Confirm "roles": ["Admin"] is present.
33+
34+
35+
36+
🧰 Blazor Authorization Setup
37+
38+
- Authentication is configured using AddMicrosoftIdentityWebApp.
39+
- Authorization policy is defined:
40+
41+
```C#
42+
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));Show more lines
43+
```
44+
45+
Razor components/pages are protected:
46+
47+
```C#
48+
@attribute [Authorize(Roles = "Admin")]
49+
```
50+
51+
🧪 Testing
52+
53+
- Test with a user assigned to the role.
54+
- Confirm access is granted to protected pages.
55+
- Test with a user not assigned to the role.
56+
- Confirm access is denied or redirected.
57+

src/aspire/AStar.Web.AppHost/AStar.Web.AppHost.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<ItemGroup>
1212
<ProjectReference Include="..\..\modules\apis\AStar.Web.ApiService\AStar.Web.ApiService.csproj"/>
13-
<ProjectReference Include="..\..\uis\AStar.Dev.Web\AStar.Dev.Web.csproj" />
13+
<ProjectReference Include="..\..\uis\AStar.Dev.Web\AStar.Dev.Web.csproj"/>
1414
</ItemGroup>
1515

1616
</Project>

src/aspire/AStar.Web.AppHost/AppHost.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@
1111
.WithReference(apiService)
1212
.WaitFor(apiService);
1313

14-
builder.Build().Run();
14+
builder.Build().Run();

src/aspire/AStar.Web.ServiceDefaults/Extensions.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,8 @@ public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder)
6464
tracing.AddSource(builder.Environment.ApplicationName)
6565
.AddAspNetCoreInstrumentation(tracing =>
6666
// Exclude health check requests from tracing
67-
tracing.Filter = context =>
68-
!context.Request.Path.StartsWithSegments(HealthEndpointPath)
69-
&& !context.Request.Path.StartsWithSegments(AlivenessEndpointPath)
67+
tracing.Filter = context => !context.Request.Path.StartsWithSegments(HealthEndpointPath)
68+
&& !context.Request.Path.StartsWithSegments(AlivenessEndpointPath)
7069
)
7170
// Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
7271
//.AddGrpcClientInstrumentation()
@@ -123,4 +122,4 @@ public static WebApplication MapDefaultEndpoints(this WebApplication app)
123122

124123
return app;
125124
}
126-
}
125+
}

src/modules/apis/AStar.Web.ApiService/AStar.Web.ApiService.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10+
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0"/>
11+
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0"/>
1012
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0"/>
1113
</ItemGroup>
1214

src/modules/apis/AStar.Web.ApiService/Program.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
1+
using Asp.Versioning;
2+
13
var builder = WebApplication.CreateBuilder(args);
24

35
// Add service defaults & Aspire client integrations.
46
builder.AddServiceDefaults();
57

68
// Add services to the container.
79
builder.Services.AddProblemDetails();
10+
_ = builder.Services.AddApiVersioning(options =>
11+
{
12+
options.DefaultApiVersion = new ApiVersion(1, 0);
13+
options.AssumeDefaultVersionWhenUnspecified = true;
14+
options.ReportApiVersions = true;
15+
})
16+
.AddApiExplorer(options =>
17+
{
18+
options.GroupNameFormat = "'v'VVV";
19+
options.SubstituteApiVersionInUrl = true;
20+
});
821

922
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
1023
builder.Services.AddOpenApi();
@@ -23,13 +36,12 @@
2336

2437
app.MapGet("/weatherforecast", () =>
2538
{
26-
var forecast = Enumerable.Range(1, 5).Select(index =>
27-
new WeatherForecast
28-
(
29-
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
30-
Random.Shared.Next(-20, 55),
31-
summaries[Random.Shared.Next(summaries.Length)]
32-
))
39+
var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast
40+
(
41+
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
42+
Random.Shared.Next(-20, 55),
43+
summaries[Random.Shared.Next(summaries.Length)]
44+
))
3345
.ToArray();
3446
return forecast;
3547
})
@@ -42,4 +54,4 @@
4254
internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
4355
{
4456
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
45-
}
57+
}
Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,43 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
4-
<TargetFramework>net10.0</TargetFramework>
5-
<ImplicitUsings>enable</ImplicitUsings>
6-
<Nullable>enable</Nullable>
4+
<Deterministic>true</Deterministic>
5+
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
6+
<UserSecretsId>d7e61e34-084f-4c2b-9be8-5925170dc3b7</UserSecretsId>
77
</PropertyGroup>
88

9+
<ItemGroup>
10+
<PackageReference Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.4.0"/>
11+
<PackageReference Include="FluentUI.Demo.Shared" Version="4.10.4"/>
12+
<PackageReference Include="JetBrains.Annotations" Version="2025.2.2"/>
13+
<PackageReference Include="Markdig" Version="0.43.0"/>
14+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.0"/>
15+
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.23.0"/>
16+
<PackageReference Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.23.0"/>
17+
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.0"/>
18+
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.13.1"/>
19+
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.13.1"/>
20+
<PackageReference Include="Microsoft.Identity.Web" Version="4.0.1"/>
21+
<PackageReference Include="Microsoft.Identity.Web.UI" Version="4.0.1"/>
22+
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0"/>
23+
<PackageReference Include="Scalar.AspNetCore" Version="2.10.3"/>
24+
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.0.16"/>
25+
<PackageReference Include="Testably.Abstractions.FileSystem.Interface" Version="10.0.0"/>
26+
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.0.0"/>
27+
<PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders" Version="1.2.0"/>
28+
</ItemGroup>
29+
30+
<ItemGroup>
31+
<Folder Include="wwwroot\assets\"/>
32+
</ItemGroup>
33+
34+
<ItemGroup>
35+
<Using Include="JetBrains.Annotations"/>
36+
</ItemGroup>
37+
938
<ItemGroup>
1039
<ProjectReference Include="..\..\aspire\AStar.Web.ServiceDefaults\AStar.Web.ServiceDefaults.csproj"/>
40+
<ProjectReference Include="..\..\modules\apis\AStar.Web.ApiService\AStar.Web.ApiService.csproj"/>
1141
</ItemGroup>
1242

1343
</Project>
Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,54 @@
1-
<!DOCTYPE html>
1+
<!DOCTYPE html>
22
<html lang="en">
33

44
<head>
55
<meta charset="utf-8"/>
66
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
77
<base href="/"/>
8-
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]"/>
98
<link rel="stylesheet" href="@Assets["app.css"]"/>
109
<link rel="stylesheet" href="@Assets["AStar.Dev.Web.styles.css"]"/>
1110
<ImportMap/>
12-
<link rel="icon" type="image/png" href="favicon.png"/>
13-
<HeadOutlet/>
11+
<link rel="icon" type="image/x-icon" href="favicon.ico"/>
12+
<HeadOutlet @rendermode="InteractiveServer"/>
13+
<script src="_content/Microsoft.FluentUI.AspNetCore.Components/js/loading-theme.js" type="text/javascript"></script>
14+
<loading-theme storage-name="theme"></loading-theme>
15+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
16+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
17+
<script>
18+
window.highlightMarkdown = () => {
19+
if (window.hljs) {
20+
window.hljs.highlightAll();
21+
}
22+
};
23+
</script>
24+
<!-- Add marked (Markdown parser) and DOMPurify (sanitizer) -->
25+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
26+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js"></script>
27+
<script>
28+
// md: markdown text
29+
// allowHtml: when true, bypass sanitizer (only do this if you trust the source)
30+
window.renderMarkdown = (md, allowHtml) => {
31+
const html = window.marked?.parse(md ?? "") ?? "";
32+
if (allowHtml) {
33+
return html;
34+
}
35+
// Sanitize, but allow common code/formatting tags and classes used by highlight.js
36+
const cfg = {
37+
ALLOWED_TAGS: [
38+
'pre', 'code', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
39+
'ul', 'ol', 'li', 'a', 'strong', 'em', 'table', 'thead', 'tbody',
40+
'tr', 'th', 'td', 'blockquote', 'hr', 'br', 'img'
41+
],
42+
ALLOWED_ATTR: ['href', 'title', 'alt', 'class', 'rel', 'target', 'src']
43+
};
44+
return window.DOMPurify ? window.DOMPurify.sanitize(html, cfg) : html;
45+
};
46+
</script>
1447
</head>
1548
1649
<body>
17-
<Routes/>
18-
<script src="@Assets["_framework/blazor.web.js"]"></script>
50+
<Routes @rendermode="InteractiveServer"/>
51+
<script src="_framework/blazor.web.js"></script>
1952
</body>
2053
21-
</html>
54+
</html>

0 commit comments

Comments
 (0)