fix: capture upload-form user id at init, not submit, so token expiry mid-session can't discard a composed release

This commit is contained in:
daniel-c-harvey
2026-06-19 23:12:26 -04:00
parent 0708bb7352
commit bd9c67fc65
@@ -129,6 +129,12 @@
// Set true once the admin has acknowledged the missing-hero warning, so a second submit proceeds.
private bool _heroWarningAcknowledged;
// Captured at component initialization (when the token is known-good) so a mid-session
// token expiry at submit time cannot discard a long-composed release. Only assigned when
// the id parses successfully — a prerender pass returns an anonymous principal (no JS
// interop available), so the field stays null until the live interactive circuit populates it.
private long? _createdByUserId;
private string _albumName = string.Empty;
private string _artist = string.Empty;
private string _genre = string.Empty;
@@ -156,6 +162,20 @@
}
}
protected override async Task OnInitializedAsync()
{
// Capture the user id once at load, while the token is known-good. The page is [Authorize]-gated
// so this should always succeed on the live interactive circuit. During static prerender, auth
// state is anonymous (no JS interop → no localStorage JWT read), so the parse fails and the
// field stays null; the interactive circuit then runs this again and sets the real value.
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
var userIdValue = authState.User.FindFirstValue(ClaimTypes.NameIdentifier);
if (long.TryParse(userIdValue, out var userId))
{
_createdByUserId = userId;
}
}
// Switching to a single-track medium collapses any multi-track selection to the first row so the
// single-track invariant holds before submit. The predicate reads the same MediumRules cardinality
// declaration the upload service enforces, so the form and the domain cannot drift.
@@ -275,13 +295,12 @@
}
}
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
var userIdValue = authState.User.FindFirstValue(ClaimTypes.NameIdentifier);
if (!long.TryParse(userIdValue, out var createdByUserId))
if (_createdByUserId is not long createdByUserId)
{
// The page is gated by [Authorize] under the Admin role, so a missing or
// unparseable id here is a configuration bug, not normal client state.
Logger.LogError("Authenticated user has no parseable NameIdentifier claim: {Value}", userIdValue);
// _createdByUserId is set at component initialization from the authenticated principal.
// A null here means the id was unavailable even at load — a genuine configuration bug,
// since the page is [Authorize]-gated.
Logger.LogError("User id was not captured at initialization — NameIdentifier claim missing or unparseable.");
_errorMessage = "Your session is missing a valid identifier. Please sign in again.";
return;
}