ASP.NET Core
Registration, configuration binding and startup validation.
ApricotFramework.PasswordHasher.AspNetCore wires the hasher into an ASP.NET Core host:
registration, configuration binding and startup validation.
It takes everything it needs from the ASP.NET Core shared framework, so it has zero NuGet dependencies and adds nothing to your dependency tree.
dotnet add package ApricotFramework.PasswordHasher.AspNetCoreNote
For a host that is not ASP.NET Core — a worker service or a console tool — use the
core package directly. new DefaultPasswordHasher(new PasswordHasherOptions { … }) needs no
container, and hashing and verification behave identically.
Registration
using ApricotFramework.PasswordHasher.AspNetCore.Extensions;
builder.Services.AddPasswordHasher(builder.Configuration);That binds the PasswordHasher configuration section, adds the options validator and registers
IPasswordHasher as a singleton. The built-in algorithms are not registered because they need
no registration — every hasher has them.
{
"PasswordHasher": {
"Algorithm": "PBKDF2-SHA512",
"Iterations": 210000
}
}Both keys are optional; omitting the section entirely gives PBKDF2-SHA512 at its default
iteration count.
Two other overloads are available — a different section name, and configuration in code:
builder.Services.AddPasswordHasher(builder.Configuration, "Security:Passwords");
builder.Services.AddPasswordHasher(options =>
{
options.Algorithm = Pbkdf2Sha256Algorithm.AlgorithmName;
options.Iterations = 600_000;
});Then inject IPasswordHasher wherever you need it.
Reloading
The registered hasher reads the options on every call through IOptionsMonitor, so a
configuration reload changes the parameters used for new hashes without restarting the host.
Stored hashes are unaffected either way.
Startup validation
Settings are validated when the host starts, not on the first login:
- an algorithm name that is not registered
- an iteration count that is zero or negative
- an iteration count below the minimum of 10,000, per NIST SP 800-63B
- an iteration count above the maximum of 10,000,000, which is the ceiling the encoded format accepts — anything the validator allows is guaranteed to be readable back
Microsoft.Extensions.Options.OptionsValidationException: Password hashing iteration count 100
is below the minimum of 10000 for 'PBKDF2-SHA512'. Existing hashes are unaffected and still
verify at their own recorded count.Warning
If you are moving from a configuration that set a very low iteration count, the host will now refuse to start until you raise it. That is the intended behaviour. It bounds only newly written hashes — existing ones are still verified at whatever count is recorded inside them, so nobody is locked out.
In a bare ServiceCollection with no host to run the startup hook, the same validation runs the
first time the options are resolved.
Replacing what is registered
IPasswordHasher is registered with TryAddSingleton, so your own registration wins:
services.AddSingleton<IPasswordHasher, MyHasher>();
services.AddPasswordHasher(builder.Configuration);Any IPasswordHashAlgorithm you register is injected into the hasher in addition to the
built-in ones:
builder.Services.AddPasswordHasherAlgorithm<MyAlgorithm>();
builder.Services.AddPasswordHasher(builder.Configuration);The generic overload lets the container construct the algorithm, so it can take constructor dependencies of its own. Adding the same type twice is a no-op. Use the instance overload when the algorithm needs arguments, or when one implementation should serve several names:
builder.Services.AddPasswordHasherAlgorithm(new Pbkdf2Sha512Algorithm("LEGACY-ALIAS"));Order relative to AddPasswordHasher does not matter — algorithms are injected as an
IEnumerable, resolved once the container is built.
Warning
Register algorithms with these methods rather than AddSingleton by hand. The
hasher and the options validator are both singletons, so an algorithm registered as scoped or
transient fails to resolve with "Cannot consume scoped service … from singleton". These
overloads pick the right lifetime for you.
Note
Adding an algorithm cannot take one away, so hashes written under any built-in name
keep verifying no matter what you register. The exception is deliberate: registering an
algorithm whose Name matches a built-in replaces that built-in, which is how you would swap
out an implementation.