Skip to content

Commit

Permalink
Self-review polish
Browse files Browse the repository at this point in the history
  • Loading branch information
josephdecock committed Aug 19, 2024
1 parent b3bd353 commit e4e7730
Show file tree
Hide file tree
Showing 20 changed files with 126 additions and 46 deletions.
5 changes: 4 additions & 1 deletion Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
<PackageReference Update="Duende.AccessTokenManagement.OpenIdConnect" Version="3.0.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Relational" Version="$(FrameworkVersionRuntime)" />
<PackageReference Update="Microsoft.Extensions.Http" Version="$(FrameworkVersionRuntime)" />
<PackageReference Update="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0" />
<PackageReference Update="Microsoft.AspNetCore.Components.WebAssembly" Version="$(FrameworkVersionRuntime)" />
<PackageReference Update="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="$(FrameworkVersionRuntime)" />
<PackageReference Update="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="$(FrameworkVersionRuntime)" />
<PackageReference Update="Microsoft.AspNetCore.Components.Authorization" Version="$(FrameworkVersionRuntime)" />
<PackageReference Update="Yarp.ReverseProxy" Version="$(YarpVersion)" />

Expand All @@ -30,6 +32,7 @@
<PackageReference Update="Microsoft.EntityFrameworkCore.InMemory" Version="$(FrameworkVersionTesting)" />
<PackageReference Update="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(FrameworkVersionTesting)" />
<PackageReference Update="Microsoft.AspNetCore.TestHost" Version="$(FrameworkVersionTesting)" />
<!-- Test timeprovider is released separately from the framework, so we can't use FrameworkVersionTesting -->
<PackageReference Update="Microsoft.Extensions.TimeProvider.Testing" Version="8.8.0" />

<PackageReference Update="Duende.IdentityServer" Version="$(IdentityServerVersion)" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
@rendermode InteractiveAuto

<CallApi Header="InteractiveAuto"></CallApi>

@code {
[CascadingParameter]
private Task<AuthenticationState>? authenticationState { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@

protected async Task CallApiAsync()
{
DisableUi = true;
apiResult = await Http.GetFromJsonAsync<ApiResult>("user-token");
DisableUi = false;
}

protected override void OnAfterRender(bool firstRender)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
@rendermode InteractiveWebAssembly

<CallApi Header="InteractiveWebAssembly"></CallApi>

@* TODO - This cascading auth state gets the auth state provider running. But
actually, we don't need the auth state provider to be running. I thought it was
weird that it wasn't, so this forces it to do so, but actually we're not doing
anything with the state changes. *@
@code {
[CascadingParameter]
private Task<AuthenticationState>? authenticationState { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@


<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />
<PackageReference Include="Microsoft.Extensions.Http" />
</ItemGroup>

<ItemGroup>
Expand Down
6 changes: 4 additions & 2 deletions samples/Blazor/PerComponent/PerComponent.Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

builder.Services.AddScoped<IRenderModeContext, ClientRenderModeContext>();

builder.Services.AddBff();
builder.Services.AddRemoteApiHttpClient("callApi");
builder.Services
.AddBffBlazorClient()
.AddCascadingAuthenticationState()
.AddRemoteApiHttpClient("callApi");

await builder.Build().RunAsync();
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
</ItemGroup>

<ItemGroup>
Expand Down
11 changes: 6 additions & 5 deletions samples/Blazor/PerComponent/PerComponent/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// BFF setup for blazor
builder.Services.AddBff()
.AddServerSideSessions()
.AddBlazorServer()
.AddRemoteApis();

builder.Services.AddCascadingAuthenticationState();

builder.Services.AddScoped<IRenderModeContext, ServerRenderModeContext>();
builder.Services.AddUserAccessTokenHttpClient("callApi", configureClient: client => client.BaseAddress = new Uri("https://localhost:5010/"));

// General blazor services
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddCascadingAuthenticationState();

// Service used by the sample to describe where code is running
builder.Services.AddScoped<IRenderModeContext, ServerRenderModeContext>();

builder.Services.AddAuthentication(options =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ RenderMode IRenderModeContext.GetMode()
if(prerendering)
{
return RenderMode.Prerender;
} else
}
else
{
return RenderMode.Server;
}
Expand Down
5 changes: 3 additions & 2 deletions samples/Blazor/WebAssembly/WebAssembly.Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

var builder = WebAssemblyHostBuilder.CreateDefault(args);

// authentication state and authorization
builder.Services.AddBff();
builder.Services
.AddBffBlazorClient() // Provides auth state provider that polls the /bff/user endpoint
.AddCascadingAuthenticationState();

await builder.Build().RunAsync();
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.4" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />
<PackageReference Include="Microsoft.Extensions.Http" />
</ItemGroup>

<ItemGroup>
Expand Down
1 change: 0 additions & 1 deletion samples/Blazor/WebAssembly/WebAssembly/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveWebAssemblyComponents();

Expand Down
10 changes: 10 additions & 0 deletions src/Duende.Bff.Blazor.Client/BffBlazorOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ public class BffBlazorOptions
/// </summary>
public string? RemoteApiBaseAddress { get; set; } = null;

/// <summary>
/// The delay, in milliseconds, before the AuthenticationStateProvider
/// will start polling the /bff/user endpoint. Defaults to 1000 ms.
/// </summary>
public int StateProviderPollingDelay { get; set; } = 1000;

/// <summary>
/// The delay, in milliseconds, between polling requests by the
/// AuthenticationStateProvider to the /bff/user endpoint. Defaults to
/// 5000 ms.
/// </summary>
public int StateProviderPollingInterval { get; set; } = 5000;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public class BffClientAuthenticationStateProvider : AuthenticationStateProvider
private DateTimeOffset _userLastCheck = DateTimeOffset.MinValue;
private ClaimsPrincipal _cachedUser = new(new ClaimsIdentity());

/// <summary>
/// An <see cref="AuthenticationStateProvider"/> intended for use in
/// Blazor WASM. It polls the /bff/user endpoint to monitor session
/// state.
/// </summary>
public BffClientAuthenticationStateProvider(
PersistentComponentState state,
IHttpClientFactory factory,
Expand All @@ -43,18 +48,22 @@ public override async Task<AuthenticationState> GetAuthenticationStateAsync()
var user = await GetUser();
var state = new AuthenticationState(user);

// checks periodically for a session state change and fires event
// this causes a round trip to the server
// adjust the period accordingly if that feature is needed
if (user!.Identity!.IsAuthenticated)
// Periodically
if (user.Identity is { IsAuthenticated: true })
{
_logger.LogInformation("starting background check..");
Timer? timer = null;

timer = new Timer(async _ =>
{
var currentUser = await GetUser(false);
// Always notify that auth state has changed, because the user management claims change over time
// Always notify that auth state has changed, because the user
// management claims (usually) change over time.
//
// Future TODO - Someday we may want an extensibility point. If the
// user management claims have been customized, then auth state
// wouldn't always change. In that case, we'd want to only fire
// if the user actually had changed.
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(currentUser)));
if (currentUser!.Identity!.IsAuthenticated == false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" />
<PackageReference Include="Microsoft.Extensions.Http" />
<!-- Explicitly taking this version so that we don't pull in vulnerable old versions. -->
<PackageReference Include="System.Text.Json" Version="8.0.4"/>
</ItemGroup>

Expand Down
15 changes: 7 additions & 8 deletions src/Duende.Bff.Blazor.Client/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Duende.Bff.Blazor.Client;

public static class ServiceCollectionExtensions
{
public static IServiceCollection AddBff(this IServiceCollection services,
public static IServiceCollection AddBffBlazorClient(this IServiceCollection services,
Action<BffBlazorOptions>? configureAction = null)
{
if (configureAction != null)
Expand All @@ -21,7 +21,6 @@ public static IServiceCollection AddBff(this IServiceCollection services,
services
.AddAuthorizationCore()
.AddScoped<AuthenticationStateProvider, BffClientAuthenticationStateProvider>()
.AddCascadingAuthenticationState()
.AddTransient<AntiforgeryHandler>()
.AddHttpClient("BffAuthenticationStateProvider", (sp, client) =>
{
Expand Down Expand Up @@ -52,7 +51,7 @@ private static string GetRemoteApiPath(IServiceProvider sp)
return opt.Value.RemoteApiPath;
}

private static Action<IServiceProvider, HttpClient> SetBaseAddressInConfigureClient(
private static Action<IServiceProvider, HttpClient> SetBaseAddress(
Action<IServiceProvider, HttpClient>? configureClient)
{
return (sp, client) =>
Expand All @@ -62,7 +61,7 @@ private static Action<IServiceProvider, HttpClient> SetBaseAddressInConfigureCli
};
}

private static Action<IServiceProvider, HttpClient> SetBaseAddressInConfigureClient(
private static Action<IServiceProvider, HttpClient> SetBaseAddress(
Action<HttpClient>? configureClient)
{
return (sp, client) =>
Expand Down Expand Up @@ -100,30 +99,30 @@ private static void SetBaseAddress(IServiceProvider sp, HttpClient client)
public static IHttpClientBuilder AddRemoteApiHttpClient(this IServiceCollection services, string clientName,
Action<HttpClient> configureClient)
{
return services.AddHttpClient(clientName, SetBaseAddressInConfigureClient(configureClient))
return services.AddHttpClient(clientName, SetBaseAddress(configureClient))
.AddHttpMessageHandler<AntiforgeryHandler>();
}

public static IHttpClientBuilder AddRemoteApiHttpClient(this IServiceCollection services, string clientName,
Action<IServiceProvider, HttpClient>? configureClient = null)
{
return services.AddHttpClient(clientName, SetBaseAddressInConfigureClient(configureClient))
return services.AddHttpClient(clientName, SetBaseAddress(configureClient))
.AddHttpMessageHandler<AntiforgeryHandler>();
}

public static IHttpClientBuilder AddRemoteApiHttpClient<T>(this IServiceCollection services,
Action<HttpClient> configureClient)
where T : class
{
return services.AddHttpClient<T>(SetBaseAddressInConfigureClient(configureClient))
return services.AddHttpClient<T>(SetBaseAddress(configureClient))
.AddHttpMessageHandler<AntiforgeryHandler>();
}

public static IHttpClientBuilder AddRemoteApiHttpClient<T>(this IServiceCollection services,
Action<IServiceProvider, HttpClient>? configureClient = null)
where T : class
{
return services.AddHttpClient<T>(SetBaseAddressInConfigureClient(configureClient))
return services.AddHttpClient<T>(SetBaseAddress(configureClient))
.AddHttpMessageHandler<AntiforgeryHandler>();
}
}
6 changes: 6 additions & 0 deletions src/Duende.Bff.Blazor/CaptureManagementClaimsCookieEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

namespace Duende.Bff.Blazor;

/// <summary>
/// This <see cref="CookieAuthenticationEvents"/> subclass invokes the BFF <see
/// cref="IClaimsService"/> to retrieve management claims and add them to the
/// session. This is useful in interactive render modes where components are
/// initialled rendered server side.
/// </summary>
public class CaptureManagementClaimsCookieEvents : CookieAuthenticationEvents
{
private readonly IClaimsService _claimsService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public static BffBuilder AddBff(this IServiceCollection services, Action<BffOpti

// cookie configuration
services.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureSlidingExpirationCheck>();
services.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureApplicationCookieRevokeRefreshToken>();

services.AddSingleton<IPostConfigureOptions<OpenIdConnectOptions>, PostConfigureOidcOptionsForSilentLogin>();

Expand Down
2 changes: 1 addition & 1 deletion src/Duende.Bff/EndpointServices/User/DefaultUserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public virtual async Task ProcessRequestAsync(HttpContext context)
{
// In blazor, it is sometimes necessary to copy management claims into the session.
// So, we don't want duplicate mgmt claims. Instead, they should overwrite the existing mgmt claims
// (in case they changed when the session slide, etc)
// (in case they changed when the session slid, etc)
var claims = (await GetUserClaimsAsync(result)).ToList();
var mgmtClaims = await GetManagementClaimsAsync(context, result);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.

using IdentityModel;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;

namespace Duende.Bff;

/// <summary>
/// Cookie configuration to revoke refresh token on logout.
/// </summary>
public class PostConfigureApplicationCookieRevokeRefreshToken : IPostConfigureOptions<CookieAuthenticationOptions>
{
private readonly BffOptions _options;
private readonly string? _scheme;
private readonly ILogger<PostConfigureApplicationCookieRevokeRefreshToken> _logger;

/// <summary>
/// ctor
/// </summary>
/// <param name="bffOptions"></param>
/// <param name="authOptions"></param>
/// <param name="logger"></param>
public PostConfigureApplicationCookieRevokeRefreshToken(IOptions<BffOptions> bffOptions, IOptions<AuthenticationOptions> authOptions, ILogger<PostConfigureApplicationCookieRevokeRefreshToken> logger)
{
_options = bffOptions.Value;
_scheme = authOptions.Value.DefaultAuthenticateScheme ?? authOptions.Value.DefaultScheme;
_logger = logger;
}

/// <inheritdoc />
public void PostConfigure(string? name, CookieAuthenticationOptions options)
{
if (_options.RevokeRefreshTokenOnLogout && name == _scheme)
{
options.Events.OnSigningOut = CreateCallback(options.Events.OnSigningOut);
}
}

private Func<CookieSigningOutContext, Task> CreateCallback(Func<CookieSigningOutContext, Task> inner)
{
async Task Callback(CookieSigningOutContext ctx)
{
_logger.LogDebug("Revoking user's refresh tokens in OnSigningOut for subject id: {subjectId}", ctx.HttpContext.User.FindFirst(JwtClaimTypes.Subject)?.Value);
await ctx.HttpContext.RevokeRefreshTokenAsync();
if (inner != null)
{
await inner.Invoke(ctx);
}
};

return Callback;
}
}

0 comments on commit e4e7730

Please sign in to comment.