Open josephdecock opened 1 year ago
It needs a BlazorSSR example with Asp.Net Core Identity, so to have the default user management solution handled.
What we need is a Blazor Web App sample that uses BFF for coordinating authentication and API calls that works for all render modes.
If possible, an example (or explanation somewhere) of how to use it with SignalR from a Blazor WASM page/component would be very welcome.
What we need is a Blazor Web App sample that uses BFF for coordinating authentication and API calls that works for all render modes.
One sample that covers all rendering modes is ideal (https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-8.0). I was thinking that it would help, for now, to target the Interactive WebAssembly, which renders on the server, as well on the client.
Is there any approximate date when we can expect the release of the updated samples?
@Liandrel , I managed to get this working with an app I am working on. While I can't show source, I can point you to the same location where I found my starting point. One caveat is that while it does work; it is not integrated into Duende at this time. https://github.com/josephdecock/InteractiveBlazorAuth/tree/main
Hope this helps
@b1tzer0, did you get "all of it" working based on that InteractiveBlazorAuth example? Based on your post, I also tried it out and run into issues, some of which are mentioned further above. For example:
Duende.Bff.DefaultLogoutService.ProcessRequestAsync(HttpContext context)
@josephdecock, I would essentially like to set up a new ASP.NET 8.0 Blazor Web App with Interactive WebAssembly (avoiding SignalR) plus a Web API and then add IdentityServer-based authentication and authorization. While your InteractiveBlazorAuth example looks like a good starting point (which does more than what I need because it demonstrates render modes that I likely don't need), it has the issues mentioned above. So, unfortunately, there is currently no adequate, working example that demonstrates the mere basics.
@ThomasBarnekow, I can confirm the /bff/user appears to work in both WASM and Blazor Server (SignalR)
Here you can see the only websocket is the browser refresh:
Logout is hit and miss, while in debug it does in fact take me to the logout page, but if I am running outside of debug mode the page just refreshes. Looking at it, I feel like I messed that up myself, when I go directly to the URL it works with no problem.
This is the code I am using for my bff authentication state provider.
public class BffAuthenticationStateProvider : AuthenticationStateProvider
{
private static readonly TimeSpan UserCacheRefreshInterval
= TimeSpan.FromSeconds(60);
private readonly HttpClient _client;
private readonly ILogger<BffAuthenticationStateProvider> _logger;
private DateTimeOffset _userLastCheck
= DateTimeOffset.FromUnixTimeSeconds(0);
private ClaimsPrincipal _cachedUser
= new ClaimsPrincipal(new ClaimsIdentity());
public BffAuthenticationStateProvider(
HttpClient client,
ILogger<BffAuthenticationStateProvider> logger)
{
_client = client;
_logger = logger;
}
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)
{
_logger.LogInformation("starting background check..");
Timer? timer = null;
timer = new Timer(async _ =>
{
var currentUser = await GetUser(false);
if (currentUser.Identity.IsAuthenticated == false)
{
_logger.LogInformation("user logged out");
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(currentUser)));
await timer.DisposeAsync();
}
}, null, 1000, 5000);
}
return state;
}
private async ValueTask<ClaimsPrincipal> GetUser(bool useCache = true)
{
var now = DateTimeOffset.Now;
if (useCache && now < _userLastCheck + UserCacheRefreshInterval)
{
_logger.LogDebug("Taking user from cache");
return _cachedUser;
}
_logger.LogDebug("Fetching user");
_cachedUser = await FetchUser();
_userLastCheck = now;
return _cachedUser;
}
record ClaimRecord(string Type, object Value);
private async Task<ClaimsPrincipal> FetchUser()
{
try
{
_logger.LogInformation("Fetching user information.");
var response = await _client.GetAsync("bff/user?slide=false");
if (response.StatusCode == HttpStatusCode.OK)
{
var claims = await response.Content.ReadFromJsonAsync<List<ClaimRecord>>();
var identity = new ClaimsIdentity(
nameof(BffAuthenticationStateProvider),
"name",
"role");
foreach (var claim in claims)
{
identity.AddClaim(new Claim(claim.Type, claim.Value.ToString()));
}
return new ClaimsPrincipal(identity);
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Fetching user failed.");
}
return new ClaimsPrincipal(new ClaimsIdentity());
}
}
Here is where I wire it up in my .Client startup file
builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<AuthenticationStateProvider, BffAuthenticationStateProvider>();
// HTTP client configuration
builder.Services.AddTransient<AntiforgeryHandler>();
builder.Services.AddHttpClient("HostUri", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<AntiforgeryHandler>();
I second this, I have been looking all over the web trying to find an example of how to properly implement SSR with Auto Mode (SignalR and WASM). I have tried various configurations with BFF and Server Side but have not had much luck. If there is a working example out there that would be greatly that we could repost or if one could be added to this repo, that would be great help in trying to solve my problem.