diff --git a/src/PowderCoating.Web/Controllers/PasskeyController.cs b/src/PowderCoating.Web/Controllers/PasskeyController.cs index e7e6013..af5ab56 100644 --- a/src/PowderCoating.Web/Controllers/PasskeyController.cs +++ b/src/PowderCoating.Web/Controllers/PasskeyController.cs @@ -15,11 +15,14 @@ namespace PowderCoating.Web.Controllers; /// Registration requires an authenticated session (user logs in once with password, /// then enrolls a passkey for future logins). Authentication is anonymous — the /// browser sends the credential before any session exists. +/// +/// Fido2 is constructed per-request from the incoming Host header so the RPID +/// matches automatically on localhost, dev, staging, and production without any +/// environment-specific configuration. /// [Route("[controller]/[action]")] public class PasskeyController : Controller { - private readonly IFido2 _fido2; private readonly UserManager _userManager; private readonly SignInManager _signInManager; private readonly ApplicationDbContext _db; @@ -29,19 +32,41 @@ public class PasskeyController : Controller private const string AuthChallengeKey = "passkey:auth:challenge"; public PasskeyController( - IFido2 fido2, UserManager userManager, SignInManager signInManager, ApplicationDbContext db, ILogger logger) { - _fido2 = fido2; _userManager = userManager; _signInManager = signInManager; _db = db; _logger = logger; } + /// + /// Builds a Fido2 instance whose RPID and origin are derived from the current + /// request, so the same code works on localhost, dev, and production unchanged. + /// + private IFido2 BuildFido2() + { + var req = HttpContext.Request; + var host = req.Host.Host; // "localhost" or "myapp.azurewebsites.net" + var port = req.Host.Port; + var origin = port.HasValue + ? $"{req.Scheme}://{host}:{port}" + : $"{req.Scheme}://{host}"; + + var config = new Fido2Configuration + { + ServerDomain = host, + ServerName = "Powder Coating Logix", + Origins = new HashSet { origin }, + TimestampDriftTolerance = 300 + }; + + return new Fido2(config); + } + // ─── Registration ──────────────────────────────────────────────────────── /// @@ -77,7 +102,7 @@ public class PasskeyController : Controller UserVerification = UserVerificationRequirement.Required }; - var options = _fido2.RequestNewCredential(new RequestNewCredentialParams + var options = BuildFido2().RequestNewCredential(new RequestNewCredentialParams { User = fidoUser, ExcludeCredentials = excludeCredentials, @@ -112,7 +137,7 @@ public class PasskeyController : Controller try { var options = CredentialCreateOptions.FromJson(optionsJson); - credential = await _fido2.MakeNewCredentialAsync(new MakeNewCredentialParams + credential = await BuildFido2().MakeNewCredentialAsync(new MakeNewCredentialParams { AttestationResponse = attestationResponse, OriginalOptions = options, @@ -155,7 +180,7 @@ public class PasskeyController : Controller [AllowAnonymous] public IActionResult LoginOptions() { - var options = _fido2.GetAssertionOptions(new GetAssertionOptionsParams + var options = BuildFido2().GetAssertionOptions(new GetAssertionOptionsParams { AllowedCredentials = [], UserVerification = UserVerificationRequirement.Required @@ -199,7 +224,7 @@ public class PasskeyController : Controller try { var options = AssertionOptions.FromJson(optionsJson); - verifyResult = await _fido2.MakeAssertionAsync(new MakeAssertionParams + verifyResult = await BuildFido2().MakeAssertionAsync(new MakeAssertionParams { AssertionResponse = assertionResponse, OriginalOptions = options, diff --git a/src/PowderCoating.Web/Program.cs b/src/PowderCoating.Web/Program.cs index bd38d8e..c978469 100644 --- a/src/PowderCoating.Web/Program.cs +++ b/src/PowderCoating.Web/Program.cs @@ -290,16 +290,9 @@ builder.Services.AddSession(options => // Add memory cache builder.Services.AddMemoryCache(); -// Register Fido2/WebAuthn for passkey (biometric) login -builder.Services.AddFido2(options => -{ - options.ServerDomain = builder.Configuration["Fido2:ServerDomain"] ?? "localhost"; - options.ServerName = builder.Configuration["Fido2:ServerName"] ?? "Powder Coating Logix"; - var origins = builder.Configuration.GetSection("Fido2:Origins").Get>(); - if (origins?.Count > 0) options.Origins = origins; - options.TimestampDriftTolerance = int.Parse( - builder.Configuration["Fido2:TimestampDriftTolerance"] ?? "300"); -}); +// Fido2/WebAuthn: no DI registration needed — PasskeyController builds a +// per-request Fido2 instance from the incoming Host header so the RPID matches +// automatically on every environment without config changes. // Configure authorization policies for multi-tenancy builder.Services.AddAuthorization(options => diff --git a/src/PowderCoating.Web/appsettings.json b/src/PowderCoating.Web/appsettings.json index 61d1343..951084a 100644 --- a/src/PowderCoating.Web/appsettings.json +++ b/src/PowderCoating.Web/appsettings.json @@ -68,12 +68,6 @@ "Enterprise": "price_enterprise_monthly_id_here" } }, - "Fido2": { - "ServerDomain": "localhost", - "ServerName": "Powder Coating Logix", - "Origins": [ "https://localhost:58461", "http://localhost:58462" ], - "TimestampDriftTolerance": 300 - }, "Storage": { "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=powdercoatingappdev;AccountKey=DN3eVfhytXb7aBC0md9h/6jE0Uzg6FJ+PK6MFc772qyqpf0kgTeXH0C2VCBBun9PiuItPd9CDKTP+ASthFCuCg==;EndpointSuffix=core.windows.net", "Containers": {