Skip to content

Commit 2d5abbc

Browse files
authored
Add a Blazor OIDC sample with Aspire (#137)
1 parent 0c41527 commit 2d5abbc

Some content is hidden

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

52 files changed

+1490
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<IsAspireHost>true</IsAspireHost>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Aspire.Hosting" Version="8.0.0-preview.1.23557.2" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\..\BlazorWebOidc\BlazorWebOidc.csproj" />
17+
<ProjectReference Include="..\..\MinimalApiJwt\MinimalApiJwt.csproj" />
18+
</ItemGroup>
19+
20+
</Project>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
var builder = DistributedApplication.CreateBuilder(args);
2+
3+
var weatherApi = builder.AddProject<Projects.MinimalApiJwt>("weatherapi");
4+
5+
builder.AddProject<Projects.BlazorWebOidc>("blazorfrontend")
6+
.WithReference(weatherApi);
7+
8+
builder.Build().Run();
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"http": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": true,
8+
"applicationUrl": "http://localhost:15053",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development",
11+
"DOTNET_ENVIRONMENT": "Development",
12+
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16258"
13+
}
14+
}
15+
}
16+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning",
6+
"Aspire.Hosting.Dcp": "Warning"
7+
}
8+
}
9+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Library</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<IsAspireSharedProject>true</IsAspireSharedProject>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
13+
14+
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.0.0" />
15+
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0-preview.1.23557.2" />
16+
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.7.0-alpha.1" />
17+
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.7.0-alpha.1" />
18+
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.6.0-beta.2" />
19+
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.6.0-beta.2" />
20+
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.6.0-beta.2" />
21+
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.5.1" />
22+
</ItemGroup>
23+
24+
</Project>
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Diagnostics.HealthChecks;
5+
using Microsoft.Extensions.Logging;
6+
using OpenTelemetry.Logs;
7+
using OpenTelemetry.Metrics;
8+
using OpenTelemetry.Trace;
9+
10+
namespace Microsoft.Extensions.Hosting;
11+
12+
public static class Extensions
13+
{
14+
public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
15+
{
16+
builder.ConfigureOpenTelemetry();
17+
18+
builder.AddDefaultHealthChecks();
19+
20+
builder.Services.AddServiceDiscovery();
21+
22+
builder.Services.ConfigureHttpClientDefaults(http =>
23+
{
24+
// Turn on resilience by default
25+
http.AddStandardResilienceHandler();
26+
27+
// Turn on service discovery by default
28+
http.UseServiceDiscovery();
29+
});
30+
31+
return builder;
32+
}
33+
34+
public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
35+
{
36+
builder.Logging.AddOpenTelemetry(logging =>
37+
{
38+
logging.IncludeFormattedMessage = true;
39+
logging.IncludeScopes = true;
40+
});
41+
42+
builder.Services.AddOpenTelemetry()
43+
.WithMetrics(metrics =>
44+
{
45+
metrics.AddRuntimeInstrumentation()
46+
.AddBuiltInMeters();
47+
})
48+
.WithTracing(tracing =>
49+
{
50+
if (builder.Environment.IsDevelopment())
51+
{
52+
// We want to view all traces in development
53+
tracing.SetSampler(new AlwaysOnSampler());
54+
}
55+
56+
tracing.AddAspNetCoreInstrumentation()
57+
.AddGrpcClientInstrumentation()
58+
.AddHttpClientInstrumentation();
59+
});
60+
61+
builder.AddOpenTelemetryExporters();
62+
63+
return builder;
64+
}
65+
66+
private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
67+
{
68+
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
69+
70+
if (useOtlpExporter)
71+
{
72+
builder.Services.Configure<OpenTelemetryLoggerOptions>(logging => logging.AddOtlpExporter());
73+
builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => metrics.AddOtlpExporter());
74+
builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());
75+
}
76+
77+
// Uncomment the following lines to enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package)
78+
// builder.Services.AddOpenTelemetry()
79+
// .WithMetrics(metrics => metrics.AddPrometheusExporter());
80+
81+
// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.Exporter package)
82+
// builder.Services.AddOpenTelemetry()
83+
// .UseAzureMonitor();
84+
85+
return builder;
86+
}
87+
88+
public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
89+
{
90+
builder.Services.AddHealthChecks()
91+
// Add a default liveness check to ensure app is responsive
92+
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
93+
94+
return builder;
95+
}
96+
97+
public static WebApplication MapDefaultEndpoints(this WebApplication app)
98+
{
99+
// Uncomment the following line to enable the Prometheus endpoint (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package)
100+
// app.MapPrometheusScrapingEndpoint();
101+
102+
// All health checks must pass for app to be considered ready to accept traffic after starting
103+
app.MapHealthChecks("/health");
104+
105+
// Only health checks tagged with the "live" tag must pass for app to be considered alive
106+
app.MapHealthChecks("/alive", new HealthCheckOptions
107+
{
108+
Predicate = r => r.Tags.Contains("live")
109+
});
110+
111+
return app;
112+
}
113+
114+
private static MeterProviderBuilder AddBuiltInMeters(this MeterProviderBuilder meterProviderBuilder) =>
115+
meterProviderBuilder.AddMeter(
116+
"Microsoft.AspNetCore.Hosting",
117+
"Microsoft.AspNetCore.Server.Kestrel",
118+
"System.Net.Http");
119+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
8+
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0" />
13+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.0" />
14+
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@implements IDisposable
2+
3+
@inject NavigationManager NavigationManager
4+
5+
<div class="nav-item px-3">
6+
<AuthorizeView>
7+
<Authorized>
8+
<form action="authentication/logout" method="post">
9+
<AntiforgeryToken />
10+
<input type="hidden" name="ReturnUrl" value="@currentUrl" />
11+
<button type="submit" class="nav-link">
12+
<span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true"></span> Logout @context.User.Identity?.Name
13+
</button>
14+
</form>
15+
</Authorized>
16+
<NotAuthorized>
17+
<a class="nav-link" href="authentication/login">
18+
<span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span> Login
19+
</a>
20+
</NotAuthorized>
21+
</AuthorizeView>
22+
</div>
23+
24+
@code {
25+
private string? currentUrl;
26+
27+
protected override void OnInitialized()
28+
{
29+
currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
30+
NavigationManager.LocationChanged += OnLocationChanged;
31+
}
32+
33+
private void OnLocationChanged(object? sender, LocationChangedEventArgs e)
34+
{
35+
currentUrl = NavigationManager.ToBaseRelativePath(e.Location);
36+
StateHasChanged();
37+
}
38+
39+
public void Dispose()
40+
{
41+
NavigationManager.LocationChanged -= OnLocationChanged;
42+
}
43+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
.bi {
2+
display: inline-block;
3+
position: relative;
4+
width: 1.25rem;
5+
height: 1.25rem;
6+
margin-right: 0.75rem;
7+
top: -1px;
8+
background-size: cover;
9+
}
10+
11+
.bi-person-badge-nav-menu {
12+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-person-badge' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 2a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3zM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0z'/%3E%3Cpath d='M4.5 0A2.5 2.5 0 0 0 2 2.5V14a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2.5A2.5 2.5 0 0 0 11.5 0h-7zM3 2.5A1.5 1.5 0 0 1 4.5 1h7A1.5 1.5 0 0 1 13 2.5v10.795a4.2 4.2 0 0 0-.776-.492C11.392 12.387 10.063 12 8 12s-3.392.387-4.224.803a4.2 4.2 0 0 0-.776.492V2.5z'/%3E%3C/svg%3E");
13+
}
14+
15+
.bi-arrow-bar-left-nav-menu {
16+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-arrow-bar-left' viewBox='0 0 16 16'%3E%3Cpath d='M12.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5ZM10 8a.5.5 0 0 1-.5.5H3.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L3.707 7.5H9.5a.5.5 0 0 1 .5.5Z'/%3E%3C/svg%3E");
17+
}
18+
19+
.nav-item {
20+
font-size: 0.9rem;
21+
padding-bottom: 0.5rem;
22+
}
23+
24+
.nav-item .nav-link {
25+
color: #d7d7d7;
26+
background: none;
27+
border: none;
28+
border-radius: 4px;
29+
height: 3rem;
30+
display: flex;
31+
align-items: center;
32+
line-height: 3rem;
33+
width: 100%;
34+
}
35+
36+
.nav-item .nav-link:hover {
37+
background-color: rgba(255,255,255,0.1);
38+
color: white;
39+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
@inherits LayoutComponentBase
2+
3+
<div class="page">
4+
<div class="sidebar">
5+
<NavMenu />
6+
</div>
7+
8+
<main>
9+
<div class="top-row px-4">
10+
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
11+
</div>
12+
13+
<article class="content px-4">
14+
@Body
15+
</article>
16+
</main>
17+
</div>
18+
19+
<div id="blazor-error-ui">
20+
An unhandled error has occurred.
21+
<a href="" class="reload">Reload</a>
22+
<a class="dismiss">🗙</a>
23+
</div>

0 commit comments

Comments
 (0)