Changelog
All notable changes to this template are documented in this file.
The format is based on Keep a Changelog, and this template adheres to Semantic Versioning.
[Unreleased]
[1.1.0] - 2026-06-30
Adds three cross-cutting infrastructure capabilities (Email/Cache, Microsoft Entra ID, Bus/Messaging) on top of DevPack 10.10.1, plus the BFF login fix. Scaffolds from
dotnet new itlab-fullstacknow include these out of the box.
Added
Materializes the abstractions shipped in DevPack 10.10.1 (the DevPack pin is bumped to 10.10.1).
- Email & cache infrastructure.
ICacheServiceimplementations (MemoryCacheService/DistributedCacheService) selected by config viaAddTemplateCache— memory by default, Redis whenConnectionStrings:Cacheis set. Email senders (SmtpEmailService/SendGridEmailService, provider switch onEmailOptions.Provider) + an HTML template renderer + a file-system template repository viaAddTemplateEmail. Generic Auth email templates (welcome, email-confirmation, password-reset) + a rebranded default layout underApi/Templates/Email, copied to the host output. NewInfrastructure.Testsproject. - Microsoft Entra ID — opt-in per-tenant scheme. Entra registered as a distinct
"EntraId"scheme alongside the own-token"Bearer"; the Smart selector routes by token issuer.ReverseLookupEntraTenantValidatorresolves the application tenant from the token's directory id (no current tenant exists during authentication).SigninWithEntraslice (local lookup byAzureAdObjectId, no auto-provisioning; issues the template's own token withidp=MicrosoftEntraID).AppUser.AzureAdObjectId/IdentityProvider+ migrationAddEntraIdentityColumns. Per-tenant Entra settings persistence (ClientSecretprotected at rest) + public-config exposure. Opt-in Graph client factory (Kiota family pinned to 1.22.2 to clear GHSA-7j59-v9qr-6fq9). Entra is off by default. - Integration event bus.
AddTemplateMessaging: the DevPack in-memory transport always (processors run in any host callingAddInfrastructure), plus the Azure Service Bus publisher (ServiceBusEventsBus, keyedEventBusType.External, System.Text.Json, routes byArgs[0]) only whenConnectionStrings:ServiceBus/ServiceBusOptions:Namespaceis set. CanonicalOrderPlacedIntegrationEventreference example: published fromOrderCreated, consumed in-memory via anINotificationHandlerand over Service Bus via anIDomainEventMessageHandler; the Worker hosts the receiver only when Service Bus is configured. Azure is off by default.
Fixed
- Login deadlock with a stale/expired session cookie (ADR-008). Reported by the first consumer: an invalid session cookie locked the user out of login —
BffAntiforgeryMiddlewarerequired a CSRF token because the cookie was present, whileGET /v1/auth/csrfrequired authentication to issue one, so the user could neither log in nor obtain a token (logout, also auth-gated, didn't help; the cookie isHttpOnly). Two complementary fixes, both template-side (no DevPack change): the middleware now validates CSRF only when the cookie authenticates the caller (Cookies.ContainsKey(...) && User.Identity.IsAuthenticated), not on mere presence — a stale cookie carries no authority, so login is no longer gated; andGET /v1/auth/csrfis nowAllowAnonymousso the SPA can obtain a token before login. Common trigger: a session cookie (noMax-Age) outliving its 8h ticket on an open tab, or Data Protection key rotation. Covered byBffAntiforgeryMiddlewareTests(unit) +AntiforgeryTests(integration metadata).
[1.0.1] - 2026-06-12
Fixes from the first real scaffold consumption (all caught by a consumer project and re-validated by the release pipeline's scaffold smoke).
Added
- Dev reverse proxy to the Vite dev server. With
Spa:DevServerUrlconfigured (shipped inappsettings.Development.json.sample), the API reverse-proxies every non-API request to Vite viaMicrosoft.AspNetCore.SpaServices.Extensions(HMR websocket included): browsehttps://localhost:52106withdotnet run+pnpm dev— same origin as production, BFF cookie works natively, no CORS. Reserved API prefixes stay out of the proxy (unknown/v1routes still 404); without the setting (production, tests) the published-SPA fallback applies unchanged.
Fixed
- pnpm 11 settings.
node-linker=hoistedlived in.npmrc, which pnpm 11 no longer reads for pnpm settings — scaffolds installed with the isolated linker (.pnpmvirtual store) and tripped AV file locks on locked-down Windows machines. NownodeLinker: hoistedinpnpm-workspace.yaml; the dead.npmrcwas removed. The msw postinstall approval also moved to its pnpm 11 home (allowBuilds, replacing the removedonlyBuiltDependencies). - Vite dev proxy targets the HTTPS endpoint. Proxying the HTTP port tripped
UseHttpsRedirection's 307; the browser followed the redirect out of the Vite origin and, withcredentials: same-origin, the loginSet-Cookiewas ignored — session 200 //v1/auth/me401. The proxy now targetshttps://localhost:52106withsecure: false.
[1.0.0] - 2026-06-12
First release published to the ITLab.Template feed as ITLab.Template.Next.Templates (install with dotnet new install ITLab.Template.Next.Templates).
Added
-
Template package (
templatepack/).ITLab.Template.Next.Templatesnupkg (PackageType=Template) so consumers install from the ITLab.Template feed (dotnet new install ITLab.Template.Next.Templates) instead of cloning. Packs.template.config+ src/tests/infra/.github + root files, shipping dotfiles and excluding bin/obj/node_modules/dist and repo-only docs/examples. Validated end-to-end: install from the nupkg → scaffold → 38 backend tests + SPA 8 tests + build green. Publishing pipeline at.azure/template-publish.yml(tagrelease/template/vX.Y.Z): build + solution tests → pack → scaffold smoke from the packed nupkg (dotnet new install, build, backend + SPA tests) → push to the ITLab.Template feed. -
Frontend SPA (ADR-007).
src/ITLab.Template.Next.Spa: React + Vite + TypeScript + Tailwind v4 + shadcn/ui, served same-origin by the API so the ADR-006 BFF cookie works without CORS —UseStaticFiles+ SPA fallback (reserved prefixes/v1,/health,/.well-known,/openapi,/scalarreturn 404, never HTML). Publish-time integration: the Api csproj builds the SPA (pnpm, override seams for CI) and bundlesdistintowwwroot; dev uses the Vite proxy. Typed API client via committed OpenAPI spec +openapi-typescript/openapi-fetch(hermetic codegen); session bootstrap fromGET /v1/auth/mewith CSRF middleware (X-XSRF-TOKEN) and global 401 handling — no token ever touches JavaScript. UI canon: login (react-hook-form + zod), protected shell with route guard, and Orders as living reference (paginated table, create dialog, approve — role-gated while the API stays the enforcement). Tests: Vitest + Testing Library + MSW (8).dotnet newscaffolds the SPA renamed end-to-end (folder, csproj SpaRoot, brand, titles), excludingnode_modules/dist. -
GET /v1/auth/me— current authenticated user (id, name, roles) projected fromICurrentUser, for SPA session hydration and UI gating; works for both cookie and Bearer schemes. -
OpenAPI response schemas. Endpoints now declare
.Produces<T>(...)(the doc previously had no response schemas —IResultis opaque);/v1/auth/csrfreturns a typedCsrfTokenResponse; Asp.Versioning ApiExplorer substitutes the version in paths (/v1/orders, not/v{version}/orders). Benefits Scalar UI and the SPA codegen alike. -
Phase 3 packaging —
dotnet new itlab-fullstack..template.config/template.jsondeclares the template withsourceName: ITLab.Template.Next, classifications Web/WebAPI/Worker/DDD/CQRS, andsources.modifiers.excludethat keepsexamples/,docs/,**/bin/,**/obj/, IDE state and per-devappsettings.Development.jsonout of generated projects. Smoke-tested end-to-end (dotnet new itlab-fullstack -n Probebuilds Debug + Release clean and passes 20/20 tests against renamed namespaces). -
Local authentication, dual-scheme (ADR-006, supersedes ADR-003; DevPack bumped to 10.4.0). The template ships with working auth — local ASP.NET Core Identity (
AppUser/AppRoleoverBaseIdentityDbContext, tables in theidentityschema without theAspNetprefix) issuing its own asymmetric (RSA) JWTs. Two schemes coexist via a forwarding selector: a BFFHttpOnlysession cookie for browsers (/v1/auth/session*, CSRF-protected viaBffAntiforgeryMiddleware+/v1/auth/csrf) and Bearer JWT validated through a published JWKS (/.well-known/jwks.json) for mobile (/v1/auth/token*, rotating refresh tokens) and service-to-service (/v1/auth/clients/token, client-credentials). Signing key is ephemeral in Development and a required PEM in production (fail-early).IdentitySeederseeds canonical roles + an optional admin fromIdentity:Admin:*. Authorization (RolePermissions+RoleBasedPermissionEvaluator) is unchanged across schemes. External-JWT resource-server (ADR-003) remains as a documented opt-in swap. -
DB-backed permission resolver.
RolePermissionentity +DbRolePermissionResolverwithIMemoryCache(5 min TTL per role). Seed viaRolePermissionConfiguration.HasData(Admin/Operator/Viewer × Orders.*) honored by both relational migrations and EF Core InMemory. Documented in ADR-004. -
Order concurrency token.
byte[] RowVersionmapped viaIsRowVersion()in the entity config plus a dedicated migrationAddOrderConcurrencyToken. Concurrent updates raiseDbUpdateConcurrencyExceptioninstead of silently overwriting; integration tests via EF InMemory cannot exercise the race (documented caveat) — cover with Testcontainers.MsSql when it matters. -
OpenTelemetry observability.
AddTemplateObservabilityhelper in Infrastructure ships baseline (resource, HttpClient, Runtime, EF Core ActivitySource, OTLP exporter); Api addsAddAspNetCoreInstrumentation()in its own composition root. Endpoint follows OTel env-var standard (OTEL_EXPORTER_OTLP_ENDPOINT, defaulthttp://localhost:4317). -
Bearer security scheme in OpenAPI.
BearerSecuritySchemeTransformerdeclares the scheme + document-wide requirement so Scalar UI auto-renders the Authorize button. -
Split health probes.
/health/live(no checks — process is alive) and/health/ready(filters checks taggedready, includesAddDbContextCheck<AppDbContext>). Both anonymous and opted out of rate limiting. -
Rate limiting. Built-in fixed-window per-IP partition with 1000 req/min default, configurable via
RateLimiting:PermitLimit/WindowSeconds./health/*opts out via.DisableRateLimiting(). -
CORS (opt-in). Registered only when
Cors:AllowedOriginscontains at least one entry; defaults allow any method/header + credentials + 10 min preflight cache. -
API versioning.
Asp.Versioning.Http10.0.0 with URL-segment primary reader (/v{version:apiVersion}/orders) andX-Api-Versionheader fallback. Default version 1.0,ReportApiVersionsenabled. Health probes stay unversioned. -
Docker + docker-compose. Multi-stage
Dockerfileper project (Api/Worker),.dockerignore,docker-compose.ymlorchestrating sql, api, worker and otel-collector. BuildKit secret mounts~/.nuget/NuGet/NuGet.Configso dotnet restore can hit the private feed inside the build. -
GitHub Actions CI workflow (
.github/workflows/ci.yml). Restore (usingAZURE_DEVOPS_PATsecret) + build Debug + build Release + tests with TRX logger + XPlat Code Coverage. -
examples/Customers/opt-in reference (ADR-005). Domain withEmail/Cpfvalue objects (Cpf delegates to DevPackGuard.Against.InvalidCpf), uniqueness pre-check + unique index, deactivation as state transition. 8-step copy recipe in the per-example README. Not shipped viadotnet new. -
Architecture Decision Records under
docs/adrs/(5 ADRs covering vertical slices, English-only source, JWT Bearer, DB-backed roles, examples opt-in). -
appsettings.Development.json.sampleas the copy-from reference (.env.samplepattern). Localappsettings.Development.jsonstays gitignored; the sample carries plausible Authority/Audience values for IdP-less local runs. -
Setup section in README with prerequisites, primeiro-run sequence, NuGet feed credential setup and Docker/docker-compose recipes.
-
CLAUDE.mdas the live contract for AI agents — vertical slices, returns/validation, domain events policy, permissions convention, authentication, observability, rate limiting, CORS, API versioning, EF, tests. -
LICENSE (ITLab proprietary internal).
Changed
- Canon
Pedidos→Orders(English-only). All identifiers, methods, events, DTOs, EF table names, migration names, endpoint paths and test class/method names refactored to English. Markdown docs (CLAUDE.md, README, docs/) intentionally remain in Portuguese. Documented in ADR-002. ITLabTemplateNextPermissions→Permissions. Short class identifier sosourceName: ITLab.Template.Nextsubstitution does not need custom symbols atdotnet newtime.- DevPack 10.2.3 → 10.3.0. Migrate
Result/ErrortoITLab.Template.DevPack.Corenamespace per the 10.3 BREAKING change. Drop four local shims now shipped by the package:ResultHttpExtensions,IdentityContextMiddleware,ScopedDispatchDomainEventsInterceptorand theTestAuthenticationHandler(latter moves to the newITLab.Template.DevPack.Testingnupkg). Orderconstructor chains: base(id)(DevPack 10.3AuditedSoftDeletableEntity<TKey>ctor).OrdersEndpointsusesMapPaginatedGet<TQuery, TDto>for the paginated GET (replaces ~26 LOC of manual query-string binding).Program.csusesapp.UseDevPackIdentityContext()instead of a local middleware to bridgeHttpContext.UserintoAsyncLocalCurrentUserAccessor.IntegrationTestFactoryinheritsWebApplicationFactoryBase<Program, AppDbContext>fromITLab.Template.DevPack.Testing— body collapses to a single class declaration. OverridesConfigureAdditionalServicesto injectJwtOptionsand callEnsureCreatedso the HasData seed populates the in-memory database.
Removed
PlaceholderAuthenticationHandlerand the#if !DEBUG / #errorgate — superseded by JWT Bearer (ADR-003).InMemoryRolePermissionResolver— superseded byDbRolePermissionResolver(ADR-004).Api/Extensions/ResultHttpExtensions.cs,Api/Middleware/IdentityContextMiddleware.cs,Infrastructure/Persistence/Interceptors/ScopedDispatchDomainEventsInterceptor.cs,tests/.../Setup/TestAuthenticationHandler.cs— all canonical in DevPack 10.3.0 now.- "Known doc-vs-binary mismatches in DevPack 10.2.3" section from CLAUDE.md — fixed in 10.3.
Phases overview (historical)
This template went through three phases before its first release:
- Phase 1 — initial skeleton. Project structure, base configuration, NuGet packages. Pre-this-changelog.
- Phase 2 — canon
Orders+ DevPack 10.3 absorption. Vertical-slice canon shipped, English-only refactor applied, JWT Bearer + DB-backed roles + observability + concurrency + Docker + CI all landed under theUnreleasedbucket above. - Phase 3 —
dotnet newpackaging..template.config/template.jsonshipped, smoke test verified end-to-end. Ready for first tagged release.