Commit graph

88 commits

Author SHA1 Message Date
406e2ff03a search all user by email at forgotten password 2026-07-04 17:24:12 +01:00
742da7c3f0 Activity moderated 2026-07-04 17:11:43 +01:00
1d26cbdf3d refacto chathub 2026-07-04 15:28:07 +01:00
ec54108cf0 presentation 2026-07-01 00:13:57 +01:00
0cc82fec5d bug fix IUserSettings 2026-06-30 23:55:43 +01:00
0965caaf40 fixes and renaming 2026-06-30 23:32:05 +01:00
1751145be8 Exploiting GitVersion 2026-06-28 14:11:26 +01:00
ee2c8452ac Navigation and DI 2026-06-26 01:45:21 +01:00
e526b050ed Add the missing Redirect.cshtml view used by LoadingPage helper
AccountController.Signin (and ExternalController / ConsentController)
return this.LoadingPage("Redirect", model.ReturnUrl) when the OIDC
client is a native one (e.g. PostIt, with a custom-scheme redirect
URI). The LoadingPage extension in Yavsc.Extensions renders
controller.View("Redirect", …), so a /Views/Shared/Redirect.cshtml
must exist.

The file was missing, and the absence surfaced as a 500 on
POST /signin once the login itself succeeded — the user authenticated
fine, Identity.Application signed in, but the response body never
rendered and the POST returned InvalidOperationException
('The view Redirect was not found'). This is what broke the PostIt
flow after the seed/IdentityResource fixes landed.

The view is the standard IdentityServer quickstart loading page: a
meta-refresh that redirects the embedded browser to the OIDC
client's callback URI (postit://callback). Localizer strings are
used so the page is translatable like the rest of the auth UI.
2026-06-26 00:21:58 +01:00
847557318f code cleanup 2026-06-25 23:55:52 +01:00
571977f81b Fix LINQ translation in EnsureDefaultApplicationScopes
EF Core was throwing at startup with:

  System.InvalidOperationException: The LINQ expression
  '[ApiResourceScopeSpecification,...].Any(s => s.ResourceName == r.Name)'
  could not be translated.

The cause: Constants.ApiResourcesScopes is a static readonly C# array,
not an IQueryable, but it was used directly inside a Where clause on an
IQueryable<ApiResource>. EF tried to translate the closure over
Constants.ApiResourcesScopes into a SQL sub-query, which is not a
supported operation.

Materialise the wanted resource names into a HashSet before letting EF
see the Where — the collection is small (5 entries) so there's no
performance reason to push it down. After this fix,
EnsureDefaultApplicationScopes runs to completion at startup and
the seed actually has a chance of doing its job (assuming the rows
aren't already present).
2026-06-25 23:20:43 +01:00
68eb24ba44 Don't seed openid/profile/offline_access as ApiScopes
IdentityServer8 refuses to start when an IdentityResource and an
ApiScope share the same Name — it throws

  Found identity scopes and API scopes that use the same names.
  This is an invalid configuration. Scopes found: openid, profile

and the host crashes before serving any request.

Constants.BuildInApiScopes has historically listed 'openid',
'profile' and 'offline_access' alongside the application scopes
(admin, moderation, performer, client). The IdentityResource
counterparts are seeded separately via
IdentityResources.OpenId().ToEntity() /
IdentityResources.Profile().ToEntity() in
EnsureDefaultApplicationScopes, so listing them again in
BuildInApiScopes produces a duplicate 'openid' / 'profile' once
that seeder is wired into MigrateDatabase and starts running on
every restart (commit be334a69). 'offline_access' is handled
directly by IdentityServer8 (DefaultResourceValidator has a
special-case branch for it) and never needs an ApiScope row.

Trim BuildInApiScopes to application scopes only. The live
ConfigurationDb already contains both IdentityResources and
ApiScopes for the same names from earlier hand-rolled SQL
bootstrap, so the duplicate-name check fires the moment the
process tries to enumerate its resources at startup.
2026-06-25 23:11:16 +01:00
bbfaa039de Set explicit defaults on seeded ApiScopes and ApiResources
The previous commit (be334a69) relied on C# defaults to populate
the Postgres NOT NULL columns Enabled, Required, Emphasize,
ShowInDiscoveryDocument (on ApiScopes) and Created (on
ApiResources). Both tables declare these columns NOT NULL without
a database default, so EF Core ends up shipping C# defaults
(false / DateTime.MinValue) that violate the constraints or
silently disable the seeded rows.

Concretely, if we deployed be334a69 as-is:

- ApiScopes.Enabled = false -> the scope is invisible to
  DefaultResourceValidator, exactly the bug we're fixing.
- ApiResources.Created = DateTime.MinValue (0001-01-01) ->
  Postgres rejects the INSERT with
  'null value in column Created violates not-null constraint'.

Set the values explicitly so the seeder produces the same state
whether it runs once or a hundred times, fresh database or not.
2026-06-25 21:30:56 +01:00
be334a69dc Seed ApiResources + ApiResourceScopes, run seeder on every startup
The previous commit (37440171) added ApiScope rows for the
application scopes (admin, moderation, performer, client, blogs).
It was a partial fix: an ApiScope alone is not a valid scope from
DefaultResourceValidator's point of view. The validator only
recognises a scope if it can find an ApiResource that exposes it
(via ApiResourceScopes). Without that link, /connect/authorize
rejects the request with 'Scope X not found in store', even
though the scope row exists. This is what killed the PostIt login
in production.

This commit:

1. Extends Constants.ApiResourcesScopes with ResourceName +
   ResourceDisplayName. Topology: one ApiResource per scope
   ('admin' resource exposes 'admin' scope, 'blogs' resource
   exposes 'blogs' scope, etc.) — keeps each scope's audience
   specific if/when we split products across separate audiences.

2. Ensures EnsureDefaultApplicationScopes also inserts the
   matching ApiResource rows (deduped on Name) and ApiResourceScope
   rows linking each resource to its scope. Idempotent: missing
   rows are added, nothing is removed.

3. Removes the b.UseSeeding(...) call inside AddConfigurationStore.
   EF Core's UseSeeding callback only fires when the database is
   empty, so on a live ConfigurationDb (which already had Clients
   and ClientScopes) it never ran — that is why the previous commit
   had no visible effect on production. The seeder is now invoked
   explicitly from MigrateDatabase via SeedConfigurationDatabase,
   which resolves ConfigurationDbContext from the DI and runs
   EnsureDefaultConfiguration on every startup, regardless of
   whether the database was fresh.

   Seeding failures are caught and logged (best-effort) so a
   misconfigured seeder cannot prevent the host from booting.

Live data on yavsc.pschneider.fr is still missing the
ApiResource/ApiResourceScope rows; a one-shot SQL or a redeploy
with this commit is needed before PostIt can log in. Production
fix to follow.
2026-06-25 21:28:13 +01:00
e76259cca2 Richer Client/Details view for admin triage
The previous Details view was a sketch: a handful of fields, a
half-broken <dt>/<dd> pairing around FrontChannelLogoutUri, and
nothing about token lifetimes, security flags, or collection sizes.
For an admin trying to understand what a given OIDC client actually
does (and why a login flow fails), that meant bouncing between the
list page and the edit page to read off half a dozen scalars.

The new view surfaces the same property surface as Edit.cshtml, but
read-only:

- Two-column layout: Identity + Security on the left, Tokens + Logout
  on the right. Security flags render as a Bootstrap 3 label
  (green/grey) so an admin can spot at a glance whether PKCE, consent,
  offline access, etc. are on or off.
- Lifetimes are formatted in human units (5 min, 2 h, 30 d) instead of
  raw seconds. Zero / unset is rendered as 'default' or '—' to avoid
  the silent-zero footgun.
- Enum-valued columns (AccessTokenType, RefreshTokenUsage,
  RefreshTokenExpiration) are rendered as their integer value since
  that's the on-disk representation in IdentityServer8.
- The Collections list is mirrored from Edit.cshtml so every nested
  editor (scopes, grant types, redirect URIs, CORS origins, IdP
  restrictions, claims, properties, secrets) is one click away.
- Secrets get a structured table: type, description, created/expiration
  timestamps, and a status badge (active / expires soon / expired /
  no expiry). Secret values are never displayed — only the freshly
  generated one, via the existing RegenerateSecret flow — and the
  note is repeated here so the table can't be misread.
- Footer promoted from inline links to a button bar (Edit, Regenerate
  secret, Back to List) for clearer call-to-action.

The ClientSecret property surface was confirmed by decompiling
IdentityServer8.EntityFramework.Storage 8.0.5: Expiration is
DateTime? (null = no expiry), Created is DateTime (default UtcNow).
No MinValue sentinel — previous draft's handling was wrong and has
been replaced by a single DateOrDash(DateTime?) helper.
2026-06-25 20:57:56 +01:00
3744017127 Seed ApiScopes for ApiResourcesScopes, align PostIt client
EnsureDefaultApplicationScopes was inserting every entry of
Constants.ApiResourcesScopes (admin, moderation, performer, client,
blogs) into the IdentityResources table, as Profile-derived rows.
That made them visible to /connect/discovery's scopes_supported
under the identity section, but no API resource would ever issue a
token bearing them — IdentityServer then rejected clients that
requested any of these scopes with 'invalid_scope' at the token
endpoint.

The most visible casualty was PostIt, a public PKCE client whose
postit-settings.json asks for scope=openid profile offline_access
blogs. 'blogs' is the scope that gates the Yavsc.Blogs deployment
(blogs.pschneider.fr), so the login flow died at the token step.

Fix:

- Constants.ApiResourcesScopes entries are now seeded as ApiScope
  rows (with Name + DisplayName). IdentityResources stays limited
  to the actual OpenID Connect profile (openid, profile).
- EnsureDefaultConfiguration gains an idempotent
  AlignPostItClientScopes pass that adds any missing scope from
  PostItScopes to the existing 'postit' client's AllowedScopes.
  Nothing is removed — manual revocation stays manual.

Existing live databases pick up both changes on next startup:
missing ApiScope rows are inserted, and the postit client's
ClientScope rows catch up.
2026-06-25 20:46:58 +01:00
99f4361e2d Api Resources seed 2026-06-25 20:22:41 +01:00
f3a3b63595 WIP PostIt login 2026-06-25 00:08:25 +01:00
2dec799d71 Introduce Yavsc.Interfaces.ISmtpClient and a recording test fake
Narrow ISmtpClient to the four operations MailSender actually uses,
behind a Yavsc.Interfaces.ISmtpClientFactory. Production wires
MailKitSmtpClient (SmtpClientFactory); tests wire a recording fake
(RecordingSmtpClientFactory). The fake is pre-registered in
WebServerFixture so SMTP calls are short-circuited; the EMailling
test now asserts the Connect -> Authenticate -> Send -> Disconnect
sequence. Yavsc.Abstract stays free of MailKit/MimeKit.
2026-06-22 02:09:02 +01:00
6aaff74082 fix(client-controller): single constructor with IHtmlLocalizer
The partial class ClientController had two constructors declared
across ClientController.cs and ClientController.Collections.cs.
ASP.NET Core DI failed to pick one at request time with:

  System.InvalidOperationException: Multiple constructors accepting
  all given argument types have been found in type
  'Yavsc.Controllers.ClientController'.

Move IHtmlLocalizer<ClientController> into the primary constructor
in ClientController.cs and drop the duplicate one in
ClientController.Collections.cs. The Collections partial now keeps
only its readonly field and action methods; the constructor and
field assignment are unified on the main file.

Also add the missing 'using Microsoft.AspNetCore.Mvc.Localization;'
to ClientController.cs so IHtmlLocalizer resolves.
2026-06-21 21:14:20 +01:00
ecac359344 yavsc-org: OAuth2 client admin editor overhaul — per-collection pages + missing fields
The OAuth2 client editor at /Client/Edit/{id} previously exposed 8
fields out of ~30 scalars and 10 collections on the IdentityServer8
Client entity. Editing the collections (RedirectUris, Scopes, Grant
Types, Cors Origins, IdP Restrictions, Claims, Properties, Secrets)
was either impossible or jammed into a single broken text input that
bound against an IEnumerable<string> property.

Restructure into per-collection subpages, each with its own
list/add/remove flow:

- RedirectUris       /Client/EditRedirectUris/{id}
- PostLogoutRedirectUris /Client/EditPostLogoutRedirectUris/{id}
- Scopes             /Client/EditScopes/{id}
- GrantTypes         /Client/EditGrantTypes/{id}
- CorsOrigins        /Client/EditCorsOrigins/{id}
- IdPRestrictions    /Client/EditIdPRestrictions/{id}
- Claims             /Client/EditClaims/{id}
- Properties         /Client/EditProperties/{id}
- Secrets            /Client/EditSecrets/{id}

Implementation:

- New partial class ClientController.Collections.cs with one
  GET/Add/Remove trio per collection. Add/Remove dispatch through
  generic helpers that handle the EF row + ClientId check.
- Shared _EditableStringList.cshtml partial consumed by the six
  single-string-field collection pages. Uses reflection to pull
  the value field and the row Id off the entity — avoids six
  nearly-identical table+form copies.
- Claims / Properties / Secrets each have their own view because
  they carry 2+ fields (Type+Value, Key+Value, or
  Type+Value+Description+Expiration).
- Main Edit.cshtml enriched: ClientId/Id hidden, all scalar
  fields split into fieldsets (Core, Security, Logout, Tokens,
  Device flow, Tokens extra), nav links to the 9 subpages with
  current row counts as badges.
- ClientController.Edit(int) GET now loads the client with all
  navigations via LoadClientAsync so the Edit.cshtml nav badges
  render real counts.

Field-correctness notes (verified by disassembling HigginsSoft
IdentityServer8.EntityFramework.Entities.Client 8.0.5-preview-net9):

- The property is PairWiseSubjectSalt, not PairwiseSubjectSalt
  (capital W on 'Wise').
- CibaLifetime and PollingInterval do NOT exist on Client in this
  IdentityServer8 version — those properties were a guess. The
  Device flow fieldset contains DeviceCodeLifetime + UserCodeType
  instead.
- AllowedIdentityTokenSigningAlgorithms and AllowAccessTokensViaBrowser
  were missing from the original form and are now exposed.
- ConsentLifetime and UserSsoLifetime are int? (nullable); the form
  binds them as plain int fields which accept empty strings.

Security:

- All new actions stay under [Authorize('AdministratorOnly')].
- Each Add/Remove takes an explicit id (Client.Id) and the row's
  ClientId is checked on the server before any delete; a rowId
  from another client returns NotFound.

Docs:

- doc/dev-tracking/client-editor-overhaul.md — inventory, status,
  follow-up ideas (confirmation prompts, validation, MVC tests).
2026-06-21 16:53:34 +01:00
d62e59ba30 postit: trailing-slash regression + loopback browser cleanup; identityserver: BC cert loader + SecurityKey
PostIt
- LoginPage renders StatusMessage as a read-only TextBox so the
  text is mouse-selectable and copyable (no copy button).
- LoginPageViewModel exposes ExternalUrl (Authentication.Authority
  with trailing slash stripped) and DiscoveryUrl
  (ExternalUrl + '/.well-known/openid-configuration'). LoginAsync
  surfaces the discovery URL before the call and suffixes it onto
  every error message, so reachability issues are diagnosable by
  pasting the URL into a browser.
- LoopbackBrowser (used for the OIDC redirect listener on desktop)
  now bounds the GetContextAsync wait at 5 minutes and calls both
  Stop() and Close() in the finally, so the listener is always
  released even if the user abandons the flow. Without this, the
  next PostIt launch fails with 'Failed to listen on prefix
  http://127.0.0.1:7890/ because it conflicts with an existing
  registration on the machine.'
- Add LoginPageViewModelTests.LoginAsync_works_when_authority_has_trailing_slash
  to lock in the discovery URL contract.
- Add LoopbackBrowserTests covering both timeout and happy-path
  listener cleanup.

Yavsc.Org
- Drop the CustomEntries['jwks_uri'] override from commit 84160f07.
  IdentityServer8 reserves that key and rejects the override with
  'Discovery custom entry jwks_uri cannot be added, because it
  already exists.' The default /.well-known/openid-configuration/jwks
  endpoint is what DiscoveryKeyEndpoint actually serves.
- Replace X509Certificate2.CreateFromPemFile + the 3-arg
  X509Certificate2(path, key, X509KeyStorageFlags) ctor with a
  BouncyCastle-backed loader. The BCL path raised
  InvalidOperationException during AddSigningCredential and aborted
  the runtime with SIGABRT (code=6/ABRT, libcoreclr.so stack) on
  the production EC Let's Encrypt cert. BouncyCastle 2.6.2
  PemReader accepts PKCS#1 + PKCS#8 EC/RSA PEMs uniformly; RSA
  path uses DotNetUtilities.ToRSA, EC path rebuilds ECDsa from
  ECParameters with the curve dispatched by NIST order bit length
  (256/384/521).
- Switch the signing credential handed to IdentityServer8 from
  X509Certificate2 to a SigningCredentials built from a SecurityKey
  (RsaSecurityKey / ECDsaSecurityKey). The cert loaded fine but
  IdentityServer8's key material service reads cert.PrivateKey at
  runtime — on Linux that handle is not retained across the
  X509Certificate2 / runtime boundary, so CreateJwkDocumentAsync
  raised NullReferenceException on the first GET /jwks. The
  SecurityKey is a pure managed object whose Key is a live
  AsymmetricAlgorithm, which survives every read IdentityServer
  does.
- Add BouncyCastle.Cryptography 2.6.2 to src/Yavsc.Org/Yavsc.Org.csproj
  and the matching PackageVersion in Directory.Packages props.
- Wrap the loader in a try/catch that prints the full managed
  stack to stderr on failure, so future PEM-format issues surface
  in journalctl instead of being hidden behind SIGABRT.
2026-06-21 07:36:22 +01:00
84160f0759 login status and histing extentions 2026-06-21 03:55:32 +01:00
5bf0480906 arbitrage 2026-06-20 20:21:15 +01:00
df431e40af nav: highlight active dropdown toggle in _LoginPartial
Add ActivePageAny(ViewContext, IEnumerable<string>) so a dropdown
toggle gets the active class + aria-current="page" whenever any of
its children is the current route. Apply it to the Plateforme,
Administration, and account menu toggles.
2026-06-20 20:17:36 +01:00
1addc3039b nav: highlight active route in _LoginPartial dropdowns
Apply PageHelpers.ActivePage to all 13 <a class="dropdown-item">
entries across the Plateforme, Administration, and account
dropdown menus so users see which section they're in. The
Logout entry matches Account/Logout specifically to avoid
colliding with the Register/Signin entries on the same Account
controller.
2026-06-20 20:07:13 +01:00
990608d4f7 nav: ActivePage is case-insensitive on controller/action
Fixes inactive Blogspot link: RouteData keeps the C# class name
"BlogSpot" while asp-controller matches "Blogspot" case-insensitively.
2026-06-20 19:58:58 +01:00
325cb339fd nav: highlight active page with active class and aria-current
Add PageHelpers.ActivePage extension and apply it to top-level nav
items and the Account/Register + Account/Signin items in _LoginPartial,
so the current route gets the active class and aria-current="page"
for accessibility.
2026-06-20 19:48:20 +01:00
f055ac45df background colors 2026-06-20 19:20:22 +01:00
28b63ce70d more accurate 2026-06-20 18:06:29 +01:00
7a0944d0f5 PostIt.Desktop: wire the loopback browser, parameterise PostIt RedirectUris
The previous commit set Platform.CreateBrowser to null on the desktop
side, so LoginAsync would still fail with 'No browser is available'.
Close that loop with an explicit desktop bootstrap.

PostIt.Desktop/PlatformBootstrap.cs mirrors the Android side: it
populates Platform.DefaultRedirectUri and Platform.CreateBrowser
once at startup. Program.Main calls EnsureInitialized before
BuildAvaloniaApp so the LoginPageViewModel sees a working browser
before any login attempt.

The Yavsc.Org seed now reads Site:ExternalUrl from configuration so
the RedirectUri list for the PostIt client follows the same setting
as the rest of the application (same value used in
Administration/ClientController, AccountController, etc.). Without
this, an embedded 'launch PostIt from a Yavsc.Org page' scenario
would be rejected by IdentityServer (redirect_uri mismatch).

BuildPostItRedirectUris is a small helper that yields the constant
PostItRedirectUris (loopback + Android custom scheme) followed by
Site:ExternalUrl when set. Both SeedNewPostItClient (fresh db) and
MigratePostItClientToPublic (existing db) consume it. The legacy
cleanup block (which used to remove https://yavsc.pschneider.fr/
and yavsc://callback) is dropped: Site:ExternalUrl is now the
canonical way to authorise that path and may legitimately equal
that value.
2026-06-20 17:49:53 +01:00
512a0ef06f PostIt: switch OIDC client from confidential (client_secret) to public (PKCE/JWT)
PostIt is a desktop/mobile app talking to Yavsc.Org
(https://yavsc.pschneider.fr) as an OIDC identity provider. The
previous grant used the client_credentials flow with a client_secret
embedded in postit-settings.json: this was both insecure (secret
travels with the binary) and inappropriate for an interactive app
(token had no user identity, so the API could not scope or audit).

The new flow is Authorization Code + PKCE:

  * PostIt client (Settings/AuthenticationSettings.cs): the
    ClientSecret property is removed; GetOidcClientOptions now drops
    the secret and accepts an optional IBrowser supplied per-platform.
  * Settings.cs: new AndroidRedirectUri constant ('android://postit-signin')
    that the Android app uses; RedirectUri is no longer hard-coded in
    MainViewModel.
  * MainViewModel.cs: the manual discovery + client_credentials POST is
    replaced with OidcClient.LoginAsync (Authorization Code + PKCE).
  * Settings sample: Authority points at the real Yavsc.Org OP, not at
    a non-existent Keycloak-style realm path.
  * Yavsc.Org/Extensions/HostingExtensions.cs: the 'postit' client seed
    is now idempotent (MigratePostItClientToPublic) and detects
    legacy state on existing ConfigurationDb rows - flips
    RequireClientSecret=false, RequirePkce=true, drops any ClientSecret
    row, and replaces the legacy RedirectUris
    (https://yavsc.pschneider.fr/, yavsc://callback) with the current
    set (http://127.0.0.1:7890/, android://postit-signin).

PostIt.Android:

  * MainActivity: explicit Name attribute so the activity alias can
    target a stable component; LaunchMode.SingleTask so the existing
    instance receives the deep-link Intent; OnNewIntent forwards the
    callback URI through AndroidOidcCallbackSink.
  * AndroidManifest.xml: activity-alias PostIt.Android.OidcCallbackActivity
    exposing scheme=android host=postit-signin to Android, so the OP
    redirect lands back in the running PostIt instance.

The IdentityModel.OidcClient.Browser.SystemBrowser package and a
thin AndroidSystemBrowser implementation are added in a follow-up so
OidcClient.LoginAsync can actually drive Chrome Custom Tabs and
consume AndroidOidcCallbackSink.
2026-06-20 17:16:07 +01:00
86c268eebd deploying the blogs 2026-06-19 23:09:43 +01:00
4398715004 reinstall fixes and reorg 2026-06-19 21:13:17 +01:00
002f8cc7e4 Split Directory.Packages.props: shared versions in root, per-product in src/
Move product-local package versions out of the root Directory.Packages.props
into per-product props files under src/<Product>/. The root file now only
contains versions for packages declared by two or more top-level products,
which is the actual shared set.

Each per-product Directory.Packages.props imports the root via
GetPathOfFileAbove so that the shared versions are inherited; this is
necessary because the .NET SDK picks the closest Directory.Packages.props
in the hierarchy and does not merge multiple ones.

Per-product file contents:
- src/cli/                    Microsoft.AspNetCore.Razor.Language,
                              Microsoft.Extensions.{CommandLineUtils,Configuration,Hosting}
- src/PostIt/                 Avalonia* and CommunityToolkit.Mvvm
- src/PostIt.Tests/           Avalonia.Headless{,XUnit}
- src/Yavsc.Org/              AsciiDocSharp*, Google.Apis.Compute.v1,
                              HigginsSoft.IdentityServer8.AspNetIdentity,
                              IdentityServer8.EntityFramework.Storage,
                              IdentityServer8.Security, IdentityServer8.Storage,
                              Microsoft.AspNetCore.Antiforgery, Authentication.Google,
                              Diagnostics.EntityFrameworkCore, Mvc.NewtonsoftJson,
                              SignalR, EntityFrameworkCore.Tools, Swashbuckle,
                              System.Security.Cryptography.Pkcs, YamlDotNet
- src/Yavsc.Org.Tests/        Microsoft.AspNetCore.Hosting,
                              Extensions.Caching.Memory, Options,
                              Options.ConfigurationExtensions,
                              Selenium.WebDriver, xunit.v3.{common,extensibility.core}
- src/Yavsc.Server/           Anthropic.SDK, Google.Apis.Calendar.v3,
                              Magick.NET-Q8-AnyCPU, MailKit, MimeKit,
                              Microsoft.AspNetCore.Http.Features, StaticFiles,
                              EntityFrameworkCore.SqlServer,
                              Npgsql.EntityFrameworkCore.PostgreSQL,
                              PayPalMerchantSDK, pazof.rules, RazorEngine.NetCore
- src/Yavsc.Web/              IdentityModel.AspNetCore

No per-product file is created for Yavsc.Api, Yavsc.Blogs, Yavsc.Abstract,
or templateWeb: Api and Blogs only declare the shared JwtBearer, Abstract
and templateWeb declare no package references at all.

Also includes a minor cosmetic update to FirstUIStript.cs (Firefox -> Chrome
driver, dedent, comment header). Tests previously failing on DataProtection
keyset / SMTP were unrelated environment issues (resolved by fixing the
SMTP password locally).
2026-06-19 18:51:18 +01:00
148f333b36 do not override appsettings-*.*.json 2026-06-19 16:18:39 +01:00
197f9f90bb a banner 2026-06-19 13:46:17 +01:00
dcf2a93ad0 Split Site:Audience into Site:ExternalUrl + Site:CorsAllowedOrigins
The Site:Audience setting was conflating two distinct concepts: an OAuth
JWT audience (a single resource identifier) and a CORS allow-list (an
array of origins). Collapsing them caused several latent bugs:
- OAuth/JWT validation expected a single string while CORS WithOrigins
  accepts an array.
- Password-reset callback URLs and OAuth client RedirectUri/Origin were
  being built from what was meant to be an audience identifier, not a
  base URL.
- Yavsc.Org's main CORS policy was hardcoded to '*', with no way to
  restrict it without code changes.

Changes:
- SiteSettings.Audience (string) replaced with CorsAllowedOrigins
  (IList<string>).
- OAuth JWT Authority still reads Site:Authority; Audience now reads
  Site:ExternalUrl (Org only; Api/Blogs use ValidateAudience=false).
- MailSender and AccountController build reset-callback URLs from
  Site:ExternalUrl.
- ClientController uses Site:ExternalUrl for OAuth RedirectUri/Origin
  defaults on newly created clients.
- Yavsc.Api and Yavsc.Blogs now read CORS origins from
  Site:CorsAllowedOrigins instead of hardcoded URLs.

Add shared AddYavscCors / AddYavscJwtBearer extension methods in
Yavsc.Server/Helpers/ServiceExtensions.cs to enforce a single
configuration contract across all runtime services (Api, Blogs, Org).
Fails closed when CorsAllowedOrigins is empty; fails fast at startup
when Site:Authority is missing.

Remove obsolete ConfigurationHelpers.GetAudience (no remaining callers).

Local appsettings-*.json files (which carry deployment-specific values
and are gitignored) must be updated to add Site:CorsAllowedOrigins.
2026-06-19 13:15:21 +01:00
508c74f3e4 cleanup 2026-06-19 02:17:07 +01:00
37ca66c636 Merge pull request #63 from pazof/dependabot/npm_and_yarn/src/Yavsc.Org/wwwroot/lib/bootstrap/npm_and_yarn-f774270336
Bump the npm_and_yarn group across 4 directories with 24 updates
2026-06-19 01:36:58 +01:00
19cf073677 Regenerate the secret 2026-06-19 01:36:08 +01:00
3508f0a55d tests OK 2026-06-18 21:43:01 +01:00
dependabot[bot]
ed50a3dd08 Bump the npm_and_yarn group across 4 directories with 24 updates
Bumps the npm_and_yarn group with 1 update in the /src/Yavsc.Org/wwwroot/lib/bootstrap directory: [vnu-jar](https://github.com/validator/validator).
Bumps the npm_and_yarn group with 1 update in the /src/Yavsc.Org/wwwroot/lib/jquery-ui directory: [browserstack-local](https://github.com/browserstack/browserstack-local-nodejs).
Bumps the npm_and_yarn group with 17 updates in the /src/Yavsc.Web/wwwroot/lib/bootstrap directory:

| Package | From | To |
| --- | --- | --- |
| [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) | `7.27.1` | `7.29.6` |
| [postcss](https://github.com/postcss/postcss) | `8.5.3` | `8.5.15` |
| [rollup](https://github.com/rollup/rollup) | `4.40.1` | `4.62.0` |
| [vnu-jar](https://github.com/validator/validator) | `24.10.17` | `26.2.1` |
| [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro) | `5.7.10` | `6.4.8` |
| [@babel/plugin-transform-modules-systemjs](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-systemjs) | `7.27.1` | `7.29.7` |
| [fast-uri](https://github.com/fastify/fast-uri) | `3.0.6` | `3.1.2` |
| [flatted](https://github.com/WebReflection/flatted) | `3.3.3` | `3.4.2` |
| [follow-redirects](https://github.com/follow-redirects/follow-redirects) | `1.15.9` | `1.16.0` |
| [form-data](https://github.com/form-data/form-data) | `4.0.2` | `4.0.6` |
| [immutable](https://github.com/immutable-js/immutable-js) | `4.3.7` | `5.1.6` |
| [lodash](https://github.com/lodash/lodash) | `4.17.21` | `4.18.1` |
| [mdast-util-to-hast](https://github.com/syntax-tree/mdast-util-to-hast) | `13.2.0` | `13.2.1` |
| [preact](https://github.com/preactjs/preact) | `10.26.5` | `10.29.2` |
| [qs](https://github.com/ljharb/qs) | `6.13.0` | `6.15.2` |
| [shell-quote](https://github.com/ljharb/shell-quote) | `1.8.2` | `1.8.4` |
| [tmp](https://github.com/raszi/node-tmp) | `0.2.3` | `0.2.7` |

Bumps the npm_and_yarn group with 2 updates in the /src/Yavsc.Web/wwwroot/lib/jquery-validation-unobtrusive directory: [minimatch](https://github.com/isaacs/minimatch) and [lodash](https://github.com/lodash/lodash).


Updates `vnu-jar` from 23.4.11 to 26.2.1
- [Release notes](https://github.com/validator/validator/releases)
- [Commits](https://github.com/validator/validator/commits)

Updates `browserstack-local` from 1.5.5 to 1.5.9
- [Release notes](https://github.com/browserstack/browserstack-local-nodejs/releases)
- [Commits](https://github.com/browserstack/browserstack-local-nodejs/compare/v1.5.5...v1.5.9)

Updates `@babel/core` from 7.27.1 to 7.29.6
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.6/packages/babel-core)

Updates `postcss` from 8.5.3 to 8.5.15
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.3...8.5.15)

Updates `rollup` from 4.40.1 to 4.62.0
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.40.1...v4.62.0)

Updates `vnu-jar` from 24.10.17 to 26.2.1
- [Release notes](https://github.com/validator/validator/releases)
- [Commits](https://github.com/validator/validator/commits)

Updates `astro` from 5.7.10 to 6.4.8
- [Release notes](https://github.com/withastro/astro/releases)
- [Changelog](https://github.com/withastro/astro/blob/astro@6.4.8/packages/astro/CHANGELOG.md)
- [Commits](https://github.com/withastro/astro/commits/astro@6.4.8/packages/astro)

Updates `js-yaml` from 4.1.0 to 4.2.0
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/commits)

Updates `@babel/plugin-transform-modules-systemjs` from 7.27.1 to 7.29.7
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.7/packages/babel-plugin-transform-modules-systemjs)

Updates `defu` from 6.1.4 to 6.1.7
- [Release notes](https://github.com/unjs/defu/releases)
- [Changelog](https://github.com/unjs/defu/blob/main/CHANGELOG.md)
- [Commits](https://github.com/unjs/defu/compare/v6.1.4...v6.1.7)

Updates `devalue` from 5.1.1 to 5.8.1
- [Release notes](https://github.com/sveltejs/devalue/releases)
- [Changelog](https://github.com/sveltejs/devalue/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/devalue/compare/v5.1.1...v5.8.1)

Updates `fast-uri` from 3.0.6 to 3.1.2
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.0.6...v3.1.2)

Updates `flatted` from 3.3.3 to 3.4.2
- [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.2)

Updates `follow-redirects` from 1.15.9 to 1.16.0
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.9...v1.16.0)

Updates `form-data` from 4.0.2 to 4.0.6
- [Release notes](https://github.com/form-data/form-data/releases)
- [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md)
- [Commits](https://github.com/form-data/form-data/compare/v4.0.2...v4.0.6)

Updates `h3` from 1.15.3 to 1.15.11
- [Release notes](https://github.com/h3js/h3/releases)
- [Changelog](https://github.com/h3js/h3/blob/v1.15.11/CHANGELOG.md)
- [Commits](https://github.com/h3js/h3/compare/v1.15.3...v1.15.11)

Updates `immutable` from 4.3.7 to 5.1.6
- [Release notes](https://github.com/immutable-js/immutable-js/releases)
- [Changelog](https://github.com/immutable-js/immutable-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/immutable-js/immutable-js/compare/v4.3.7...v5.1.6)

Updates `lodash` from 4.17.21 to 4.18.1
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.18.1)

Updates `mdast-util-to-hast` from 13.2.0 to 13.2.1
- [Release notes](https://github.com/syntax-tree/mdast-util-to-hast/releases)
- [Commits](https://github.com/syntax-tree/mdast-util-to-hast/compare/13.2.0...13.2.1)

Updates `preact` from 10.26.5 to 10.29.2
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.26.5...10.29.2)

Updates `qs` from 6.13.0 to 6.15.2
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.13.0...v6.15.2)

Updates `shell-quote` from 1.8.2 to 1.8.4
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/shell-quote/compare/v1.8.2...v1.8.4)

Updates `tmp` from 0.2.3 to 0.2.7
- [Changelog](https://github.com/raszi/node-tmp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/raszi/node-tmp/compare/v0.2.3...v0.2.7)

Updates `vite` from 6.3.4 to 7.3.5
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.5/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.5/packages/vite)

Updates `minimatch` from 5.1.0 to 5.1.8
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v5.1.0...v5.1.8)

Updates `lodash` from 4.17.21 to 4.18.1
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.18.1)

---
updated-dependencies:
- dependency-name: vnu-jar
  dependency-version: 26.2.1
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: browserstack-local
  dependency-version: 1.5.9
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: "@babel/core"
  dependency-version: 7.29.6
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: postcss
  dependency-version: 8.5.15
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: rollup
  dependency-version: 4.62.0
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: vnu-jar
  dependency-version: 26.2.1
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: astro
  dependency-version: 6.4.8
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: js-yaml
  dependency-version: 4.2.0
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: "@babel/plugin-transform-modules-systemjs"
  dependency-version: 7.29.7
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: defu
  dependency-version: 6.1.7
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: devalue
  dependency-version: 5.8.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: fast-uri
  dependency-version: 3.1.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: flatted
  dependency-version: 3.4.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: follow-redirects
  dependency-version: 1.16.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: form-data
  dependency-version: 4.0.6
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: h3
  dependency-version: 1.15.11
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: immutable
  dependency-version: 5.1.6
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: mdast-util-to-hast
  dependency-version: 13.2.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: preact
  dependency-version: 10.29.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: qs
  dependency-version: 6.15.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: shell-quote
  dependency-version: 1.8.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: tmp
  dependency-version: 0.2.7
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: vite
  dependency-version: 7.3.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: minimatch
  dependency-version: 5.1.8
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-18 01:26:31 +00:00
94fb98eff6 doc, doc and doc. 2026-06-18 02:18:49 +01:00
c1f4d19975 fices the UI 2026-06-15 02:55:22 +01:00
4bfc28a369 Test using "make test" 2026-06-15 00:46:50 +01:00
5c6d6ee687 refactoring 2026-06-15 00:33:38 +01:00
aabbbc1fea use the helper 2026-06-15 00:11:48 +01:00
69e81b9fd4 Giving up at UI testing 2026-06-15 00:00:39 +01:00
cba18df870 Fixes the css 2026-06-14 23:04:24 +01:00