Prep backend for .NET 10: replace obsolete hosting/crypto APIs - #4347
Conversation
Part of #4254. Migrates Program.cs from the obsolete WebHost/ IWebHostBuilder hosting model to WebApplicationBuilder, and replaces the obsolete Rfc2898DeriveBytes constructor in PasswordHash with the static Pbkdf2 method. Both APIs are already obsolete-marked under .NET 9/10 (ASPDEPR008, SYSLIB0060) and TreatWarningsAsErrors turns them into build failures once the TargetFramework is bumped. Neither change depends on .NET 10 itself, so landing them now shrinks the scope of the eventual TFM-bump PR. Verified: builds clean on net8.0, full Backend.Tests suite passes (1175/1175), including PasswordHashTests confirming byte-identical hash output.
📝 WalkthroughWalkthroughThe PR updates PBKDF2 password derivation to use UTF-8 input with the static API, migrates application startup to Changes.NET runtime updates
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related issues
Sequence Diagram(s)sequenceDiagram
participant Main
participant WebApplicationBuilder
participant Startup
participant WebApplication
Main->>WebApplicationBuilder: CreateBuilder(args)
Main->>Startup: ConfigureServices(builder.Services)
WebApplicationBuilder->>WebApplication: Build()
Main->>Startup: Configure(app, environment, lifetime)
Startup->>Startup: Resolve ILogger<Startup>
Main->>WebApplication: Run()
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #4347 +/- ##
===========================================
+ Coverage 75.96% 87.22% +11.26%
===========================================
Files 305 57 -248
Lines 11384 5081 -6303
Branches 1411 617 -794
===========================================
- Hits 8648 4432 -4216
+ Misses 2332 506 -1826
+ Partials 404 143 -261
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Backend/Program.cs`:
- Around line 14-16: Update the startup logger initialization around
LoggerFactory.Create and Startup so it reuses the WebApplicationBuilder logging
pipeline and providers instead of creating a separate console-only factory. Pass
the shared ILoggerFactory-derived Startup logger while preserving the existing
configuration and provider registrations from builder.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: b05cc5c3-91b3-4bab-a41c-3f76e3fc8c58
📒 Files selected for processing (2)
Backend/Helper/PasswordHash.csBackend/Program.cs
Building the startup-time ILoggerFactory with LoggerFactory.Create only wired up Console, so Startup's early log calls (JWT secret, CAPTCHA/email config checks) silently skipped the Debug/EventSource/ EventLog providers that WebApplicationBuilder registers by default for the rest of the app. Build it instead from a copy of WebApplicationBuilder's own service collection, so it shares the exact same provider configuration.
CreateStartupLoggerFactory discarded the ServiceProvider it built after pulling out the ILoggerFactory, so the intermediate provider (and any disposable singletons it created) was never disposed. Now CreateStartupServiceProvider returns the provider itself, scoped to a using block that ends right after Startup.Configure returns, so cleanup happens deterministically before app.Run() rather than being left to GC.
hahn-kev
left a comment
There was a problem hiding this comment.
It looks good, though I do have a suggestion.
Startup's constructor no longer takes ILogger<Startup>, so Program no longer needs to build a throwaway service provider just to obtain one before the app exists. - Settings configuration uses AddOptions<Settings>().Configure<T>() so the environment-variable helpers get the real app logger, injected. - Configure() resolves its logger from app.ApplicationServices. - The JWT secret check writes to stderr before throwing; no logging pipeline exists at that point in ConfigureServices. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
imnasnainaec
left a comment
There was a problem hiding this comment.
@imnasnainaec reviewed all commit messages, resolved 1 discussion, and dismissed @hahn-kev from a discussion.
Reviewable status: 0 of 3 files reviewed, all discussions resolved (waiting on hahn-kev).
jasonleenaylor
left a comment
There was a problem hiding this comment.
On the strength of Kevin's approval
@jasonleenaylor reviewed 3 files and all commit messages, and made 1 comment.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on hahn-kev).
Summary
Prep work toward #4254 (Update .NET from 8 to 10). Replaces two APIs that are already obsolete-marked as of
.NET 9/10 and would fail the build outright once the TargetFramework is bumped, since the project builds with
TreatWarningsAsErrors=true. Neither replacement requires .NET 10, so landing them separately shrinks the scope ofthe eventual TFM-bump PR.
No behavior changes: same password hashes, same service registrations, same middleware pipeline, same log messages.
Backend/Helper/PasswordHash.cs—SYSLIB0060The
Rfc2898DeriveBytesconstructor is obsolete. Replaced with the staticRfc2898DeriveBytes.Pbkdf2(...)method,passing the password as UTF-8 bytes. The old constructor also used UTF-8 internally, so existing stored hashes
continue to validate.
Backend/Program.cs—ASPDEPR008WebHost.CreateDefaultBuilder(...).UseStartup<Startup>()is obsolete.Mainnow usesWebApplicationBuilderminimal hosting and invokes
Startup's two methods directly:CreateWebHostBuilderis removed; it had no other callers.Backend/Startup.csThe old hosting model injected
ILogger<Startup>intoStartup's constructor, resolving it from the host'sinternal service provider. Minimal hosting has no equivalent provider, and one can't be conjured before
builder.Build()without building a throwaway container. Rather than do that, each logger use now gets the realapplication logger at the point where one is actually available:
IConfiguration; the_loggerfield is gone.Settingsconfiguration (the only pre-Build()code path that logs) moves fromservices.Configure<Settings>(options => ...)toservices.AddOptions<Settings>().Configure<ILogger<Startup>>((options, logger) => ...). The callback already ranlazily — on first
IOptions<Startup.Settings>resolution, after the app is built — so this only changes where itslogger comes from.
CheckedEnvironmentVariableandCheckedEnvironmentVariablePositiveIntare nowstaticandtake the logger as a parameter.
Configure(...)resolvesILogger<Startup>fromapp.ApplicationServicesand passes it toCreateAdminUser.ConfigureServices, where no logging pipelineexists yet. It writes to stderr and then throws, as before.
Every consumer of
Startup.SettingsinjectsIOptions<Startup.Settings>(singleton), notIOptionsSnapshotorIOptionsMonitor, so the configure callback still runs exactly once and the environment-variable warnings are notrepeated per scope.
Verification
dotnet build— clean, 0 warnings.dotnet test— 1175 passed.npm run fmt-backend-check— clean.No admin user name provided, skipped admin creationappear asBackendFramework.Startuprecords through the application's logging pipeline; unsettingCOMBINE_JWT_SECRET_KEYprints the stderr message before the exception. Neither path is covered by unit tests.
Created by Claude Sonnet 5 and Claude Opus 5
Devin: https://app.devin.ai/review/sillsdev/TheCombine/pull/4347
This change is