From f2ae01729a821bef023627b9439f8e5447a499b8 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Mon, 6 Jul 2026 19:19:28 +0100 Subject: [PATCH] clean up --- .vscode/settings.json | 3 +- doc/dev-tracking/client-editor-overhaul.md | 278 --------------------- 2 files changed, 2 insertions(+), 279 deletions(-) delete mode 100644 doc/dev-tracking/client-editor-overhaul.md diff --git a/.vscode/settings.json b/.vscode/settings.json index 65938a22..6e22cb5e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -34,7 +34,8 @@ "copilotcli/claude-haiku-4.5", "copilotcli/gpt-4.1", "copilotcli/gpt-5-mini", - "copilotcli/mai-code-1-flash-picker" + "copilotcli/mai-code-1-flash-picker", + "copilotcli/gpt-5.3-codex" ] } } diff --git a/doc/dev-tracking/client-editor-overhaul.md b/doc/dev-tracking/client-editor-overhaul.md deleted file mode 100644 index 0231e6aa..00000000 --- a/doc/dev-tracking/client-editor-overhaul.md +++ /dev/null @@ -1,278 +0,0 @@ -# Client editor overhaul — Yavsc.Org administration - -## Goal - -Bring the OAuth2 client administration UI (`/Client/Edit/{id}` and friends) -in Yavsc.Org to feature parity with the IdentityServer8 `Client` entity -model. Today the editor only exposes a handful of scalar fields and a few -single-line inputs for collections; the bulk of the entity and its -related collections are unreachable from the UI. - -## Inventory — current state - -### Properties exposed by `Views/Client/Edit.cshtml` - -| Field | Type | Notes | -| ------------------------ | ----------- | ---------------------------------- | -| `ClientId` | string | hidden, identifier | -| `Enabled` | bool | checkbox | -| `ClientName` | string | display name | -| `FrontChannelLogoutUri` | string | only front-channel, no back-channel | -| `RedirectUris` | collection | rendered as a single text input | -| `IdentityTokenLifetime` | int | seconds | -| `AbsoluteRefreshTokenLifetime` | int | seconds | -| `ClientSecrets` | collection | rendered as a single text input | -| `AccessTokenType` | enum | dropdown (custom `SetAppTypesInputValues`) | - -### Properties of `IdentityServer8.EntityFramework.Entities.Client` **NOT** in the editor - -Core scalars (16 fields missing): - -- `Description` -- `ClientUri` -- `LogoUri` -- `RequireConsent` -- `RequirePkce` -- `RequireRequestObject` -- `RequireClientSecret` -- `AllowPlainTextPkce` -- `AllowOfflineAccess` -- `AllowRememberConsent` -- `AlwaysIncludeUserClaimsInIdToken` -- `AlwaysSendClientClaims` -- `AuthorizationCodeLifetime` -- `BackChannelLogoutUri` -- `BackChannelLogoutSessionRequired` -- `CibaLifetime` -- `ClientClaimsPrefix` -- `ConsentLifetime` -- `Created` -- `DeviceCodeLifetime` -- `EnableLocalLogin` -- `Enabled` -- `FrontChannelLogoutSessionRequired` -- `IncludeJwtId` -- `LastAccessed` -- `LogoUri` -- `NonEditable` -- `PairwiseSubjectSalt` -- `PollingInterval` -- `ProtocolType` -- `RefreshTokenExpiration` -- `RefreshTokenUsage` -- `SlidingRefreshTokenLifetime` -- `UpdateAccessTokenClaimsOnRefresh` -- `Updated` -- `UserCodeType` -- `UserSsoLifetime` - -Collections (8 missing — currently either not exposed at all, or jammed -into a single-line text input that doesn't work for an IEnumerable): - -- `AllowedGrantTypes` → `ClientGrantType` (GrantType) -- `AllowedScopes` → `ClientScope` (Scope) -- `RedirectUris` → `ClientRedirectUri` (RedirectUri) — exposed but broken -- `PostLogoutRedirectUris` → `ClientPostLogoutRedirectUri` (PostLogoutRedirectUri) -- `AllowedCorsOrigins` → `ClientCorsOrigin` (Origin) -- `IdentityProviderRestrictions` → `ClientIdPRestriction` (Provider) -- `Claims` → `ClientClaim` (Type, Value) -- `Properties` → `ClientProperty` (Key, Value) -- `ClientSecrets` → `ClientSecret` (Type, Value, Description, Created, Expiration) — exposed but broken -- `AllowedSigningAlgorithms` → scalar string collection on Client itself - -## Pages to add - -Pattern: one Razor page per collection under -`Views/Client/Edit{Collection}.cshtml`. Each page lists existing rows, -offers an "Add" form with the relevant fields, and a per-row -remove button. The main `Edit.cshtml` becomes a hub page with links -to each subpage plus the scalar fields it already has. - -| Page | Route | Form fields | -| ------------------------------------- | ------------------------------------------ | ------------------------------------------------- | -| `Edit.cshtml` | `GET /Client/Edit/{id}` (existing) | scalar fields + nav links | -| `EditRedirectUris.cshtml` | `GET /Client/EditRedirectUris/{id}` | `RedirectUri` | -| `EditPostLogoutRedirectUris.cshtml` | `GET /Client/EditPostLogoutRedirectUris/{id}` | `PostLogoutRedirectUri` | -| `EditScopes.cshtml` | `GET /Client/EditScopes/{id}` | `Scope` (with select of known scopes) | -| `EditGrantTypes.cshtml` | `GET /Client/EditGrantTypes/{id}` | `GrantType` (with select of known types) | -| `EditCorsOrigins.cshtml` | `GET /Client/EditCorsOrigins/{id}` | `Origin` | -| `EditIdPRestrictions.cshtml` | `GET /Client/EditIdPRestrictions/{id}` | `Provider` | -| `EditClaims.cshtml` | `GET /Client/EditClaims/{id}` | `Type`, `Value` | -| `EditProperties.cshtml` | `GET /Client/EditProperties/{id}` | `Key`, `Value` | -| `EditSecrets.cshtml` (replacement) | `GET /Client/EditSecrets/{id}` | `Type`, `Value`, `Description`, `Expiration` | - -Partial view `_EditableList.cshtml` factored once and consumed by all -of the above. - -## Controller actions to add - -For each collection `Foo`: - -- `GET EditFoo(int id)` — load the client, render the page -- `POST AddFoo(int id, …)` — append a row, redirect to `EditFoo` -- `POST RemoveFoo(int id, int rowId)` — delete a row, redirect - -## Verification - -- `dotnet build src/Yavsc.Org/Yavsc.Org.csproj` → 0 errors -- No tests in `Yavsc.Org.Tests` exercise the controller today (per - `find … -name "ClientController*" -not -path "*/bin/*"`). Smoke-test - by logging in as admin, hitting `/Client/Edit/1`, then each - `Edit*/1` page, and verifying the add/remove POSTs. -- Existing seed flow (`MigratePostItClientToPublic` in - `HostingExtensions.cs`) must keep working — the editor changes are - additive, not destructive. - -## Out of scope - -- Tests (no MVC test infrastructure currently exists for this controller) -- Migration of existing collection fields (the broken `RedirectUris` - text input will simply be replaced by the new subpage) -- Per-collection authorization policies (the controller is already - `[Authorize("AdministratorOnly")]`) -- Client cloning / templating / JSON import-export - -## Status - -2026-06-21 16:04 — kickoff. Inventory done. Pages not yet started. - -2026-06-21 16:11 — first delivery, **build does not compile by design** -(per Paul: "Tu peux même me laisser un travail qui ne compile -pas"). The structural work is done; the residual errors are easy -fixes Paul will do in a debug session. - -Files added (working tree, not yet committed): - -- `src/Yavsc.Org/Controllers/Administration/ClientController.Collections.cs` - — partial class with the per-collection GET / Add / Remove actions. -- `src/Yavsc.Org/Views/Client/EditRedirectUris.cshtml` -- `src/Yavsc.Org/Views/Client/EditPostLogoutRedirectUris.cshtml` -- `src/Yavsc.Org/Views/Client/EditScopes.cshtml` -- `src/Yavsc.Org/Views/Client/EditGrantTypes.cshtml` -- `src/Yavsc.Org/Views/Client/EditCorsOrigins.cshtml` -- `src/Yavsc.Org/Views/Client/EditIdPRestrictions.cshtml` -- `src/Yavsc.Org/Views/Client/EditClaims.cshtml` -- `src/Yavsc.Org/Views/Client/EditProperties.cshtml` -- `src/Yavsc.Org/Views/Client/EditSecrets.cshtml` -- `src/Yavsc.Org/Views/Client/_EditableStringList.cshtml` - — partial consumed by the single-string-field collection pages. - -Files modified: - -- `src/Yavsc.Org/Controllers/Administration/ClientController.cs` - — `class` → `partial class`; the `Edit(int id)` GET now uses - `LoadClientAsync` to load all navigations (so the new Edit.cshtml - can render counts in its nav links). -- `src/Yavsc.Org/Views/Client/Edit.cshtml` - — significantly enriched: nav links to the 9 sub-pages, all the - scalar fields split into fieldsets (Security, Logout, Tokens, - Device / CIBA, Tokens-extra), ClientId / Id hidden. - -### Known residual compile errors (4 errors total) - -Paul is fixing these in a debug session. The structure is sound; the -errors are missing properties on the `Client` entity, a Razor -nullable quirk, and a `Localizer` injection miss. - -1. `Edit.cshtml:249` — `PairwiseSubjectSalt` doesn't exist on - `IdentityServer8.EntityFramework.Entities.Client`. **Fix**: drop - the field from Edit.cshtml; IdentityServer8 likely uses a - different property name (e.g. on a related entity) or doesn't - expose it. -2. `Edit.cshtml:221` — `CibaLifetime` doesn't exist on `Client`. - **Fix**: same as above. CIBA flow may be configured elsewhere - (resource-level) or via a different property. -3. `ClientController.Collections.cs` lines 181, 217, 253, 304 — - `Localizer` is not available in the partial class. **Fix**: inject - `IStringLocalizer` via the constructor, or - inline the strings ("BothTypeAndValueRequired", "KeyRequired", - "ValueRequired", "SecretValueRequired"). -4. `EditSecrets.cshtml:44` — `s.Expiration?.ToString("u")` on a - `DateTime?`. **Fix**: just `s.Expiration?.ToString("u")` works - if you write `s.Expiration.Value.ToString("u")`, or use - `(s.Expiration is null ? "" : s.Expiration.Value.ToString("u"))`, - or `s.Expiration?.ToString("u") ?? string.Empty`. - -### Suggested next session - -Once the 4 compile errors are fixed and the pages render: - -1. Smoke test by logging in as admin, hitting `/Client/Edit/1`, - then each `Edit*/1` page, and verifying add/remove POSTs. -2. Add a confirmation prompt (or 2-step form) for Remove actions — - removing a Redirect URI is destructive and one click is too easy. -3. Wire up some collection-level validation (e.g. redirect URI must - be a valid URL) at the controller level. -4. Add tests — the project doesn't have MVC test infrastructure - today; consider adding a `Yavsc.Org.Tests` project that drives - the controller via `WebApplicationFactory`. - - -## Test bootstrap notes (session of 2026-06-21 17:00+) - -When adding new integration tests against `WebServerFixture`: - -1. **Skip `/Account/Login` roundtrip.** The fixture ships without - `MapRazorPages()` (commented out in `HostingExtensions.ConfigurePipeline`), - so `/Identity/Account/Login` is 404, and the custom - `/Account/Login` route requires a complex antiforgery dance. - Instead, build a `ClaimsPrincipal` for the test user via - `UserManager` + `IUserClaimsPrincipalFactory`, - then call `IAuthenticationService.SignInAsync` on a synthetic - `DefaultHttpContext` and replay the resulting `Set-Cookie` header - into the test `HttpClient`. See - `ClientControllerCollectionTests.IssueIdentityCookie`. - -2. **Create the `Administrator` role before assigning it.** ASP.NET - Identity stores roles in `AspNetRoles`; there is no automatic seed. - The constant name is `YavscConstants.AdminGroupName` = `"Administrator"`. - Use `RoleManager.CreateAsync(new IdentityRole("Administrator"))` - before `AddToRoleAsync`. - -3. **Use `InMemory` connection string to bypass the prod signing-cert - requirement.** `HostingExtensions.AddIdentityServer` requires a - PEM cert unless `builder.Environment.IsDevelopment()` OR - `UsesInMemoryProvider(connectionString)`. The fixture already - uses `InMemory`, so `AddDeveloperSigningCredential()` is called - automatically — but only after we wired this check in (see - commit history). - -4. **Field-name gotchas** (from disassembling HigginsSoft - IdentityServer8.EntityFramework.Entities.Client 8.0.5-preview-net9): - - `PairWiseSubjectSalt` (capital W on "Wise"), not `PairwiseSubjectSalt`. - - `CibaLifetime` and `PollingInterval` do NOT exist on `Client` in - this version. - - `ConsentLifetime` and `UserSsoLifetime` are `int?`. - -5. **`MapStaticAssets()` fails on test projects.** Calling - `MapStaticAssets()` resolves a manifest file - (`.staticwebassets.endpoints.json`) that test projects - don't produce. Skip when `WebRootPath` points at the test - assembly directory. - -6. **Routing 404 on /Client/Edit/{id} via WebServerFixture.** As of - this session, the GET endpoint returns 404 even with admin - header. The route mapping is intact - (`MapDefaultControllerRoute()`), so this is likely an MVC - convention routing issue with the - `Controllers/Administration/` subdirectory. To investigate - next session: log middleware pipeline or hit `/Client` index - first to see if any Client route resolves. - -7. **`MapStaticAssets()` is unconditional in prod, but blocks tests.** - `WebApplication.CreateBuilder` defaults `ContentRootPath` to - `AppContext.BaseDirectory`. In test runs that resolves to - `src/Yavsc.Org.Tests/bin/Debug/net10.0/`, where - `Yavsc.Org.Tests.staticwebassets.endpoints.json` doesn't exist - (it's generated only by projects with the Web SDK). The - `app.MapStaticAssets()` call inside `ConfigurePipeline` then - throws and the fixture fails to start — taking every test in - the `[Collection("Yavsc Server")]` down with it. - - This is a pre-existing fragility of the WebServerFixture that - the new test work surfaced. Fixing it cleanly requires either: - (a) moving the test project to the Web SDK so it produces its - own manifest, (b) copying the manifest at build time via an - MSBuild target, or (c) routing `MapStaticAssets` through an - assembly-resolution fallback. None attempted in this session — - recorded for next session.