Skip to content

Commit

Permalink
working
Browse files Browse the repository at this point in the history
  • Loading branch information
afedyanin committed Oct 20, 2023
1 parent fe51065 commit 1629f39
Show file tree
Hide file tree
Showing 30 changed files with 454 additions and 338 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ Keycloak for Blazor demo
- [Persist additional claims and tokens from external providers in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/additional-claims?view=aspnetcore-7.0)


## Additional
- [Server-side ASP.NET Core Blazor additional security scenarios](https://learn.microsoft.com/en-us/aspnet/core/blazor/security/server/additional-scenarios?view=aspnetcore-7.0)
- [OIDC authentication in server-side Blazor](https://stackoverflow.com/questions/64853618/oidc-authentication-in-server-side-blazor)
- [Authentication and Authorization](https://gist.github.com/SteveSandersonMS/175a08dcdccb384a52ba760122cd2eda)
- [BlazorWebAssemblyCookieAuth](https://github.com/berhir/BlazorWebAssemblyCookieAuth)

## Keycloak setup

```
Expand Down
23 changes: 5 additions & 18 deletions src/BlazorApp/App.razor
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
@if (context.User.Identity?.IsAuthenticated != true)
{
<RedirectToLogin />
}
else
{
<p role="alert">You are not authorized to access this resource.</p>
}
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
</CascadingAuthenticationState>
10 changes: 0 additions & 10 deletions src/BlazorApp/BFF/AntiforgeryHandler.cs

This file was deleted.

103 changes: 0 additions & 103 deletions src/BlazorApp/BFF/BffAuthenticationStateProvider.cs

This file was deleted.

4 changes: 1 addition & 3 deletions src/BlazorApp/Pages/Counter.razor
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>
<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

Expand Down
46 changes: 0 additions & 46 deletions src/BlazorApp/Pages/CurrentUser.razor

This file was deleted.

12 changes: 4 additions & 8 deletions src/BlazorApp/Pages/FetchData.razor
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
@page "/fetchdata"
@using BlazorWasm.Shared
@inject HttpClient Http

@attribute [Authorize]

<PageTitle>Weather forecast</PageTitle>
@page "/fetchdata"
@using BlazorShared
@inject Services.FetchWeatherForecastService WeatherService

<h1>Weather forecast</h1>

Expand Down Expand Up @@ -44,6 +40,6 @@ else

protected override async Task OnInitializedAsync()
{
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
forecasts = await WeatherService.GetPublicWeatherForeacast();
}
}
62 changes: 62 additions & 0 deletions src/BlazorApp/Pages/FetchProtectedData.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@page "/fetchprotecteddata"
@using BlazorShared;
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject Services.FetchWeatherForecastService WeatherService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from the server.</p>

@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<div>
<button @onclick="OnRefreshClick">Refresh</button>
</div>

<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}

@code {
private WeatherForecast[]? forecasts;

protected override async Task OnInitializedAsync()
{
await LoadData();
}

private async Task OnRefreshClick()
{
await LoadData();
}

private async Task LoadData()
{
forecasts = null;
forecasts = await WeatherService.GetProtectedWeatherForeacast();
}
}
16 changes: 14 additions & 2 deletions src/BlazorApp/Pages/Index.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
@page "/"

<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>

<CurrentSession />
Welcome to your new app.

<SurveyPrompt Title="How is Blazor working for you?" />

<AuthorizeView>
<Authorized>
<strong>Hello, @context!.User!.Identity!.Name!</strong>
<a href="Account/Logout">Log out</a>
</Authorized>
<NotAuthorized>
<a href="Account/Login">Log in</a>
</NotAuthorized>
</AuthorizeView>
23 changes: 12 additions & 11 deletions src/BlazorApp/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using BlazorApp.Services;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Components.Authorization;
using BlazorApp.BFF;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace BlazorApp;

Expand All @@ -13,16 +14,16 @@ public static async Task Main(string[] args)
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

// authentication state plumbing
builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, BffAuthenticationStateProvider>();

// HTTP client configuration
builder.Services.AddTransient<AntiforgeryHandler>();

builder.Services.AddHttpClient("backend", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<AntiforgeryHandler>();
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("backend"));
builder.Services.TryAddSingleton<AuthenticationStateProvider, HostAuthenticationStateProvider>();
builder.Services.TryAddSingleton(sp => (HostAuthenticationStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());
builder.Services.AddTransient<AuthorizedHandler>();
builder.Services.AddHttpClient("default", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
builder.Services.AddHttpClient("authorizedClient")
.AddHttpMessageHandler<AuthorizedHandler>();
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("default"));
builder.Services.AddTransient<FetchWeatherForecastService>();

await builder.Build().RunAsync();
}
Expand Down
38 changes: 38 additions & 0 deletions src/BlazorApp/Services/AuthorizedHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Net;

namespace BlazorApp.Services;

public class AuthorizedHandler : DelegatingHandler
{
private readonly HostAuthenticationStateProvider _authenticationStateProvider;

public AuthorizedHandler(HostAuthenticationStateProvider authenticationStateProvider)
{
_authenticationStateProvider = authenticationStateProvider;
}

protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
HttpResponseMessage responseMessage;
if (!authState!.User!.Identity!.IsAuthenticated)
{
// if user is not authenticated, immediately set response status to 401 Unauthorized
responseMessage = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
else
{
responseMessage = await base.SendAsync(request, cancellationToken);
}

if (responseMessage.StatusCode == HttpStatusCode.Unauthorized)
{
// if server returned 401 Unauthorized, redirect to login page
_authenticationStateProvider.SignIn();
}

return responseMessage;
}
}
Loading

0 comments on commit 1629f39

Please sign in to comment.