The server no longer returns an EmailNotConfirmed flag. Remove the
emailNotConfirmed sentinel from AuthService, the EmailNotConfirmed
observable property and branch from LoginViewModel, and drop the
Visibility binding from the resend button so it is always shown in
the login form regardless of the failure reason.
The server no longer returns an EmailNotConfirmed flag; it returns a
uniform 401 for all login failures. Remove the flag-gated branch and show
the resend-confirmation link whenever the login form has an error message,
so users with an unconfirmed account still have a clear path forward.
Auth endpoints had no IP-level rate limit; only Identity's per-account
lockout applied. A sliding-window policy (20 requests / 15 min per IP,
no queue) is now registered in Program.cs and applied to AuthController
via [EnableRateLimiting]. Rejected requests receive 429 Too Many Requests.
AddDefaultIdentity was called without configuring PasswordOptions or
LockoutOptions, relying on undocumented Identity defaults. Minimum
password length is now 12 with digit, upper, lower, and non-alphanumeric
requirements. Failed-login lockout is explicitly set to 5 attempts over a
15-minute window.
Returning EmailNotConfirmed=true after a correct password was submitted
disclosed both that the email address is registered and that the password
is valid, without completing authentication. The endpoint now returns a
uniform 401 Bad credentials in all failure cases. Clients should offer a
resend-confirmation link unconditionally via POST /auth/email/resend.
The sub claim was hardcoded to the literal string "TokenForTheApiWithAuth"
for every token, making it useless for identifying the principal. It now
holds user.Id, matching the JWT spec and enabling standard token inspection
and future revocation workflows.
JWT signing key and database connection string were committed in plaintext.
Both are now empty placeholders; real values live in .NET User Secrets (dev)
and must be supplied via environment variables or a secrets manager in production.
CheckAdminRole() and CheckUberadminRole() in all admin ViewModels now
call JwtService.IsTokenValid() before trusting role claims, so an
expired token no longer passes the client-side admin gate.
AuthService.IsAuthenticated() also validates token expiry instead of
only checking for a non-empty token string.
Every admin delete action (companies, machines, software, hardware
entities, publications, reference data, and media assets) now shows a
confirmation dialog naming the item before calling the API, preventing
accidental permanent deletion from a single misclick.
Replace the mix of Segoe MDL2 codepoints, SymbolIcon elements, and raw
emoji used for admin grid action buttons (edit, delete, description,
merge, photos, videos, etc.) with the same Google Material Icons glyphs
used by the Blazor MudBlazor admin pages, sourced from the already
bundled MaterialIcons-Regular.ttf font. Also fixes the Releases action
icon, which was using the codepoint for "album" instead of "new_releases".
Several strings used in AdminSoftwarePage.xaml (external IDs, similar
software, markdown description fields, release management button)
existed in the .resx resources but had no corresponding property on
LocalizedStrings, so the bindings resolved to nothing at runtime.
Platform/genre/company suggestion lists in AdminWwpcImportReviewViewModel
sorted alphabetically after substring filtering, which could push an
exact match (e.g. "Windows") off the visible list when many other
entries shared the same prefix. Now exact matches rank first, followed
by Jaro-Winkler similarity, then alphabetical order.
Duplicate groups now render as explicit cards instead of relying on
ListView/GroupStyle grouping, which Uno doesn't render reliably across
targets. The merge-target picker no longer loads the entire 170k-row
Software table into memory; it now reuses the existing
SoftwareService.SearchForPickerAsync search endpoint, same as the
sibling similar-software picker. The merge panel also now shows a
"Merging X into Y" summary and locks the target (with a Change link)
when pre-selected from a chosen master, matching the Blazor page's UX.
Staging a photo in the Uno admin photo pages (GPU/Machine/Processor/
SoundSynth/Software covers/screenshots) threw ArgumentOutOfRangeException
inside Microsoft.Kiota.Serialization.Json when deserializing the
admin/pending response. Blazor's equivalent dialog never hit this because
it bypasses Kiota entirely for the upload call (raw JS fetch). Replaced
the Kiota-generated PostAsync calls with a manual HttpClient POST +
System.Text.Json deserialization for these endpoints, and hardened
ImageSourceFactory's DispatcherQueue.TryEnqueue calls to surface a real
exception instead of hanging silently if enqueueing fails.
Also fixed the staged-photo cards appearing permanently stuck on
"uploading..." even after a successful stage: their DataTemplates had no
x:DefaultBindMode, so all x:Bind expressions defaulted to OneTime and
never reflected the item's status/thumbnail/progress changes.
Machine, GPU, Processor, and Sound Synth detail pages now show photos
as a card grid (ItemsRepeater + UniformGridLayout), matching the
Blazor app's style and the grid pattern already used for software
screenshots/promo art. Company logos carousel is unchanged.
ResponsiveView's Visibility was bound to IsLoading, keeping it Collapsed
during the first layout pass. It only picks its narrow/wide template based
on the width it measures the first time it participates in layout, so it
never rendered until a real SizeChanged (device rotation) forced it to
re-evaluate. Reorder it under the loading/error overlays instead of hiding
it, so it always measures correctly from the start, and drop the ineffective
QueueLayoutRefresh workaround.
AndroidManifest.xml was missing the INTERNET permission, which .NET
for Android auto-injects only for Debug builds, causing all network
requests to silently fail in signed Release builds. Also make the
embedded appsettings.json fallback in BuildConfiguration always seed
first on Android instead of gating on an unreliable File.Exists check
against APK-packaged content assets.
StackPanel gives its children infinite height during measurement, so the
inner ScrollViewer/TextBox never received a finite height to scroll
against and the boxes just grew unbounded instead of scrolling. Replace
the StackPanel wrappers with Grid (Auto/*) rows across all admin
description editors, and add a matching edit+preview pane to
AdminSoftwarePage, which previously had no preview and no scrolling.
Screenshots were managed from a standalone admin page listing every
screenshot across all software, with an in-page picker just to choose
upload scope. Convert it to the same child-of-Software pattern used by
Versions/Releases/PromoArt/Videos: reached via a row icon on the
Software admin grid, scoped to that software via navigation params,
with a back button returning to the Software list. Drops the now
redundant Software picker and the standalone sidebar entry.
MarkdownTextBlock crashed with a NullReferenceException in ThemeListener
under Uno, and the WebView2-based fallback used by description pages
doesn't composite on Linux desktop. Render markdown directly to XAML
elements (TextBlock/Span/Hyperlink/Image trees) via Markdig's AST instead,
shared by MessageMarkdownView and all entity description pages, with no
embedded browser dependency on any platform.
The Messages page already had an IsAdmin flag and the server already
supported a "reports" folder of system-authored conversations, but
there was no button/command in the Uno app to ever navigate to it, so
admins had no way to see system messages in the Messages area.
Add the junction sections that were missing from the Uno app's
read-only company viewer (smartphones, tablets, PDAs, machine
families, GPUs, processors, sound synthesizers, software, books,
documents, magazines), and restructure the page into tabs
(Overview/Hardware/Software & Media/People/Description) following
the app's established detail-page tab pattern.
The 6.7.0-dev.99 prerelease pulled in by global.json regressed the
CommunityToolkit Carousel control on the Uno desktop/Skia target: item
containers rendered but their content never did, breaking the Logo
History and Photos carousels. 6.5.36 is the documented stable version
and restores correct rendering.