Skip to content
This repository was archived by the owner on Jan 24, 2025. It is now read-only.

Commit e4e7730

Browse files
committed
Self-review polish
1 parent b3bd353 commit e4e7730

20 files changed

+126
-46
lines changed

Directory.Build.targets

+4-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
<PackageReference Update="Duende.AccessTokenManagement.OpenIdConnect" Version="3.0.0" />
1818
<PackageReference Update="Microsoft.EntityFrameworkCore.Relational" Version="$(FrameworkVersionRuntime)" />
1919
<PackageReference Update="Microsoft.Extensions.Http" Version="$(FrameworkVersionRuntime)" />
20-
<PackageReference Update="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0" />
20+
<PackageReference Update="Microsoft.AspNetCore.Components.WebAssembly" Version="$(FrameworkVersionRuntime)" />
21+
<PackageReference Update="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="$(FrameworkVersionRuntime)" />
22+
<PackageReference Update="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="$(FrameworkVersionRuntime)" />
2123
<PackageReference Update="Microsoft.AspNetCore.Components.Authorization" Version="$(FrameworkVersionRuntime)" />
2224
<PackageReference Update="Yarp.ReverseProxy" Version="$(YarpVersion)" />
2325

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

3538
<PackageReference Update="Duende.IdentityServer" Version="$(IdentityServerVersion)" />
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
11
@rendermode InteractiveAuto
22

33
<CallApi Header="InteractiveAuto"></CallApi>
4-
5-
@code {
6-
[CascadingParameter]
7-
private Task<AuthenticationState>? authenticationState { get; set; }
8-
}

samples/Blazor/PerComponent/PerComponent.Client/Components/CallApi.razor

+2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737

3838
protected async Task CallApiAsync()
3939
{
40+
DisableUi = true;
4041
apiResult = await Http.GetFromJsonAsync<ApiResult>("user-token");
42+
DisableUi = false;
4143
}
4244

4345
protected override void OnAfterRender(bool firstRender)
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
11
@rendermode InteractiveWebAssembly
22

33
<CallApi Header="InteractiveWebAssembly"></CallApi>
4-
5-
@* TODO - This cascading auth state gets the auth state provider running. But
6-
actually, we don't need the auth state provider to be running. I thought it was
7-
weird that it wasn't, so this forces it to do so, but actually we're not doing
8-
anything with the state changes. *@
9-
@code {
10-
[CascadingParameter]
11-
private Task<AuthenticationState>? authenticationState { get; set; }
12-
}

samples/Blazor/PerComponent/PerComponent.Client/PerComponent.Client.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111

1212
<ItemGroup>
13-
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.0" />
14-
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
13+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />
14+
<PackageReference Include="Microsoft.Extensions.Http" />
1515
</ItemGroup>
1616

1717
<ItemGroup>

samples/Blazor/PerComponent/PerComponent.Client/Program.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

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

10-
builder.Services.AddBff();
11-
builder.Services.AddRemoteApiHttpClient("callApi");
10+
builder.Services
11+
.AddBffBlazorClient()
12+
.AddCascadingAuthenticationState()
13+
.AddRemoteApiHttpClient("callApi");
1214

1315
await builder.Build().RunAsync();

samples/Blazor/PerComponent/PerComponent/PerComponent.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.1" />
10+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
1111
</ItemGroup>
1212

1313
<ItemGroup>

samples/Blazor/PerComponent/PerComponent/Program.cs

+6-5
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,21 @@
77

88
var builder = WebApplication.CreateBuilder(args);
99

10-
// Add services to the container.
10+
// BFF setup for blazor
1111
builder.Services.AddBff()
1212
.AddServerSideSessions()
1313
.AddBlazorServer()
1414
.AddRemoteApis();
15-
16-
builder.Services.AddCascadingAuthenticationState();
17-
18-
builder.Services.AddScoped<IRenderModeContext, ServerRenderModeContext>();
1915
builder.Services.AddUserAccessTokenHttpClient("callApi", configureClient: client => client.BaseAddress = new Uri("https://localhost:5010/"));
2016

17+
// General blazor services
2118
builder.Services.AddRazorComponents()
2219
.AddInteractiveServerComponents()
2320
.AddInteractiveWebAssemblyComponents();
21+
builder.Services.AddCascadingAuthenticationState();
22+
23+
// Service used by the sample to describe where code is running
24+
builder.Services.AddScoped<IRenderModeContext, ServerRenderModeContext>();
2425

2526
builder.Services.AddAuthentication(options =>
2627
{

samples/Blazor/PerComponent/PerComponent/ServerRenderModeContext.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ RenderMode IRenderModeContext.GetMode()
1010
if(prerendering)
1111
{
1212
return RenderMode.Prerender;
13-
} else
13+
}
14+
else
1415
{
1516
return RenderMode.Server;
1617
}

samples/Blazor/WebAssembly/WebAssembly.Client/Program.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
var builder = WebAssemblyHostBuilder.CreateDefault(args);
55

6-
// authentication state and authorization
7-
builder.Services.AddBff();
6+
builder.Services
7+
.AddBffBlazorClient() // Provides auth state provider that polls the /bff/user endpoint
8+
.AddCascadingAuthenticationState();
89

910
await builder.Build().RunAsync();

samples/Blazor/WebAssembly/WebAssembly.Client/WebAssembly.Client.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.4" />
13-
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.4" />
14-
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
12+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
13+
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />
14+
<PackageReference Include="Microsoft.Extensions.Http" />
1515
</ItemGroup>
1616

1717
<ItemGroup>

samples/Blazor/WebAssembly/WebAssembly/Program.cs

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
var builder = WebApplication.CreateBuilder(args);
77

8-
// Add services to the container.
98
builder.Services.AddRazorComponents()
109
.AddInteractiveWebAssemblyComponents();
1110

src/Duende.Bff.Blazor.Client/BffBlazorOptions.cs

+10
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ public class BffBlazorOptions
1919
/// </summary>
2020
public string? RemoteApiBaseAddress { get; set; } = null;
2121

22+
/// <summary>
23+
/// The delay, in milliseconds, before the AuthenticationStateProvider
24+
/// will start polling the /bff/user endpoint. Defaults to 1000 ms.
25+
/// </summary>
2226
public int StateProviderPollingDelay { get; set; } = 1000;
27+
28+
/// <summary>
29+
/// The delay, in milliseconds, between polling requests by the
30+
/// AuthenticationStateProvider to the /bff/user endpoint. Defaults to
31+
/// 5000 ms.
32+
/// </summary>
2333
public int StateProviderPollingInterval { get; set; } = 5000;
2434
}

src/Duende.Bff.Blazor.Client/BffClientAuthenticationStateProvider.cs

+14-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ public class BffClientAuthenticationStateProvider : AuthenticationStateProvider
2121
private DateTimeOffset _userLastCheck = DateTimeOffset.MinValue;
2222
private ClaimsPrincipal _cachedUser = new(new ClaimsIdentity());
2323

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

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

5457
timer = new Timer(async _ =>
5558
{
5659
var currentUser = await GetUser(false);
57-
// Always notify that auth state has changed, because the user management claims change over time
60+
// Always notify that auth state has changed, because the user
61+
// management claims (usually) change over time.
62+
//
63+
// Future TODO - Someday we may want an extensibility point. If the
64+
// user management claims have been customized, then auth state
65+
// wouldn't always change. In that case, we'd want to only fire
66+
// if the user actually had changed.
5867
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(currentUser)));
5968

6069
if (currentUser!.Identity!.IsAuthenticated == false)

src/Duende.Bff.Blazor.Client/Duende.Bff.Blazor.Client.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
1111
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" />
1212
<PackageReference Include="Microsoft.Extensions.Http" />
13+
<!-- Explicitly taking this version so that we don't pull in vulnerable old versions. -->
1314
<PackageReference Include="System.Text.Json" Version="8.0.4"/>
1415
</ItemGroup>
1516

src/Duende.Bff.Blazor.Client/ServiceCollectionExtensions.cs

+7-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Duende.Bff.Blazor.Client;
1010

1111
public static class ServiceCollectionExtensions
1212
{
13-
public static IServiceCollection AddBff(this IServiceCollection services,
13+
public static IServiceCollection AddBffBlazorClient(this IServiceCollection services,
1414
Action<BffBlazorOptions>? configureAction = null)
1515
{
1616
if (configureAction != null)
@@ -21,7 +21,6 @@ public static IServiceCollection AddBff(this IServiceCollection services,
2121
services
2222
.AddAuthorizationCore()
2323
.AddScoped<AuthenticationStateProvider, BffClientAuthenticationStateProvider>()
24-
.AddCascadingAuthenticationState()
2524
.AddTransient<AntiforgeryHandler>()
2625
.AddHttpClient("BffAuthenticationStateProvider", (sp, client) =>
2726
{
@@ -52,7 +51,7 @@ private static string GetRemoteApiPath(IServiceProvider sp)
5251
return opt.Value.RemoteApiPath;
5352
}
5453

55-
private static Action<IServiceProvider, HttpClient> SetBaseAddressInConfigureClient(
54+
private static Action<IServiceProvider, HttpClient> SetBaseAddress(
5655
Action<IServiceProvider, HttpClient>? configureClient)
5756
{
5857
return (sp, client) =>
@@ -62,7 +61,7 @@ private static Action<IServiceProvider, HttpClient> SetBaseAddressInConfigureCli
6261
};
6362
}
6463

65-
private static Action<IServiceProvider, HttpClient> SetBaseAddressInConfigureClient(
64+
private static Action<IServiceProvider, HttpClient> SetBaseAddress(
6665
Action<HttpClient>? configureClient)
6766
{
6867
return (sp, client) =>
@@ -100,30 +99,30 @@ private static void SetBaseAddress(IServiceProvider sp, HttpClient client)
10099
public static IHttpClientBuilder AddRemoteApiHttpClient(this IServiceCollection services, string clientName,
101100
Action<HttpClient> configureClient)
102101
{
103-
return services.AddHttpClient(clientName, SetBaseAddressInConfigureClient(configureClient))
102+
return services.AddHttpClient(clientName, SetBaseAddress(configureClient))
104103
.AddHttpMessageHandler<AntiforgeryHandler>();
105104
}
106105

107106
public static IHttpClientBuilder AddRemoteApiHttpClient(this IServiceCollection services, string clientName,
108107
Action<IServiceProvider, HttpClient>? configureClient = null)
109108
{
110-
return services.AddHttpClient(clientName, SetBaseAddressInConfigureClient(configureClient))
109+
return services.AddHttpClient(clientName, SetBaseAddress(configureClient))
111110
.AddHttpMessageHandler<AntiforgeryHandler>();
112111
}
113112

114113
public static IHttpClientBuilder AddRemoteApiHttpClient<T>(this IServiceCollection services,
115114
Action<HttpClient> configureClient)
116115
where T : class
117116
{
118-
return services.AddHttpClient<T>(SetBaseAddressInConfigureClient(configureClient))
117+
return services.AddHttpClient<T>(SetBaseAddress(configureClient))
119118
.AddHttpMessageHandler<AntiforgeryHandler>();
120119
}
121120

122121
public static IHttpClientBuilder AddRemoteApiHttpClient<T>(this IServiceCollection services,
123122
Action<IServiceProvider, HttpClient>? configureClient = null)
124123
where T : class
125124
{
126-
return services.AddHttpClient<T>(SetBaseAddressInConfigureClient(configureClient))
125+
return services.AddHttpClient<T>(SetBaseAddress(configureClient))
127126
.AddHttpMessageHandler<AntiforgeryHandler>();
128127
}
129128
}

src/Duende.Bff.Blazor/CaptureManagementClaimsCookieEvents.cs

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
namespace Duende.Bff.Blazor;
88

9+
/// <summary>
10+
/// This <see cref="CookieAuthenticationEvents"/> subclass invokes the BFF <see
11+
/// cref="IClaimsService"/> to retrieve management claims and add them to the
12+
/// session. This is useful in interactive render modes where components are
13+
/// initialled rendered server side.
14+
/// </summary>
915
public class CaptureManagementClaimsCookieEvents : CookieAuthenticationEvents
1016
{
1117
private readonly IClaimsService _claimsService;

src/Duende.Bff/Configuration/BffServiceCollectionExtensions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public static BffBuilder AddBff(this IServiceCollection services, Action<BffOpti
5555

5656
// cookie configuration
5757
services.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureSlidingExpirationCheck>();
58+
services.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureApplicationCookieRevokeRefreshToken>();
5859

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

src/Duende.Bff/EndpointServices/User/DefaultUserService.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public virtual async Task ProcessRequestAsync(HttpContext context)
8080
{
8181
// In blazor, it is sometimes necessary to copy management claims into the session.
8282
// So, we don't want duplicate mgmt claims. Instead, they should overwrite the existing mgmt claims
83-
// (in case they changed when the session slide, etc)
83+
// (in case they changed when the session slid, etc)
8484
var claims = (await GetUserClaimsAsync(result)).ToList();
8585
var mgmtClaims = await GetManagementClaimsAsync(context, result);
8686

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) Duende Software. All rights reserved.
2+
// See LICENSE in the project root for license information.
3+
4+
using IdentityModel;
5+
using Microsoft.AspNetCore.Authentication;
6+
using Microsoft.AspNetCore.Authentication.Cookies;
7+
using Microsoft.Extensions.Logging;
8+
using Microsoft.Extensions.Options;
9+
using System;
10+
using System.Threading.Tasks;
11+
12+
namespace Duende.Bff;
13+
14+
/// <summary>
15+
/// Cookie configuration to revoke refresh token on logout.
16+
/// </summary>
17+
public class PostConfigureApplicationCookieRevokeRefreshToken : IPostConfigureOptions<CookieAuthenticationOptions>
18+
{
19+
private readonly BffOptions _options;
20+
private readonly string? _scheme;
21+
private readonly ILogger<PostConfigureApplicationCookieRevokeRefreshToken> _logger;
22+
23+
/// <summary>
24+
/// ctor
25+
/// </summary>
26+
/// <param name="bffOptions"></param>
27+
/// <param name="authOptions"></param>
28+
/// <param name="logger"></param>
29+
public PostConfigureApplicationCookieRevokeRefreshToken(IOptions<BffOptions> bffOptions, IOptions<AuthenticationOptions> authOptions, ILogger<PostConfigureApplicationCookieRevokeRefreshToken> logger)
30+
{
31+
_options = bffOptions.Value;
32+
_scheme = authOptions.Value.DefaultAuthenticateScheme ?? authOptions.Value.DefaultScheme;
33+
_logger = logger;
34+
}
35+
36+
/// <inheritdoc />
37+
public void PostConfigure(string? name, CookieAuthenticationOptions options)
38+
{
39+
if (_options.RevokeRefreshTokenOnLogout && name == _scheme)
40+
{
41+
options.Events.OnSigningOut = CreateCallback(options.Events.OnSigningOut);
42+
}
43+
}
44+
45+
private Func<CookieSigningOutContext, Task> CreateCallback(Func<CookieSigningOutContext, Task> inner)
46+
{
47+
async Task Callback(CookieSigningOutContext ctx)
48+
{
49+
_logger.LogDebug("Revoking user's refresh tokens in OnSigningOut for subject id: {subjectId}", ctx.HttpContext.User.FindFirst(JwtClaimTypes.Subject)?.Value);
50+
await ctx.HttpContext.RevokeRefreshTokenAsync();
51+
if (inner != null)
52+
{
53+
await inner.Invoke(ctx);
54+
}
55+
};
56+
57+
return Callback;
58+
}
59+
}

0 commit comments

Comments
 (0)