Themia.SecondFactor
0.63.1
dotnet add package Themia.SecondFactor --version 0.63.1
NuGet\Install-Package Themia.SecondFactor -Version 0.63.1
<PackageReference Include="Themia.SecondFactor" Version="0.63.1" />
<PackageVersion Include="Themia.SecondFactor" Version="0.63.1" />
<PackageReference Include="Themia.SecondFactor" />
paket add Themia.SecondFactor --version 0.63.1
#r "nuget: Themia.SecondFactor, 0.63.1"
#:package Themia.SecondFactor@0.63.1
#addin nuget:?package=Themia.SecondFactor&version=0.63.1
#tool nuget:?package=Themia.SecondFactor&version=0.63.1
Themia.SecondFactor
Multi-method second factor for an app's sign-in: passkey, TOTP, recovery codes, email OTP and SMS OTP, with a per-app and per-role policy
(MfaPolicyOptions), method strength (passkey, TOTP and recovery codes are strong; email and SMS are weak and never claim mfa alone),
an admin reset with a role-level act-on rule, and a break-glass reset kept in the database. Storage is yours to choose through
IMfaMethodStore; Themia.SecondFactor.PostgreSql is the shipped store. Wire it with AddThemiaSecondFactor(...) and give it an
ISecondFactorSecretProtector (Data Protection or a KMS) and an ISecondFactorContactResolver.
Importing existing TOTP secrets (no re-enrol)
An app that already holds TOTP secrets can move its users over without making them scan a new QR code. Copy each secret into the store once, under the protection scheme it has today, and let the protector read that scheme:
- Implement
ISecondFactorSecretProtectorsoUnprotect(scheme, protectedSecret)handles every scheme still in the data: the old one and the oneProtectwrites now (Scheme). Returnnullfor a scheme it does not know; verification then fails closed (SecondFactorOutcome.NotAvailable) instead of guessing. - Add one method per user with
IMfaMethodStore.AddAsync(new NewMfaMethod { ... }):Kind = MfaMethodKind.Totp,SecretProtected= the stored value as it is (do not decrypt and re-encrypt),ProtectionScheme= the scheme that protects it,Label,EnrolledAt. - Carry the replay position: set
LastUsedStepto the last accepted time step (Unix seconds / 30) if the old system recorded one; leave it at-1only when it did not. A code of that step or earlier is then refused asReplayed. - Keep the old scheme readable for as long as any imported row uses it; the scheme is stored per row, so old and new coexist.
Limits to check before running the import:
- Secret length: Themia.Totp rejects a secret that decodes to fewer than 16 bytes (RFC 4226 §4, 128 bits) by throwing when it is used. Older systems often issued 80-bit secrets (16 base32 characters); those cannot be imported as they are, and those users must re-enrol. The import does not check the length: a short secret is stored, then throws at sign-in (not a clean refusal). Screen every secret's decoded length before importing and leave the short ones to re-enrol.
- Digits, period and algorithm: one
TotpOptionsapplies to every user (defaults: 6 digits, 30-second step, SHA-1, what authenticator apps use). Set them to match the old system before importing; secrets issued with different parameters cannot be mixed. - The method id is new. The replay record is keyed on the new method id, so import before the first sign-in, not during it.
tests/Themia.SecondFactor.IntegrationTests/LegacyTotpImportTests.cs runs this recipe against Postgres: a legacy-scheme secret verifies,
the imported LastUsedStep blocks a replay, and an unreadable scheme fails closed.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Dapper (>= 2.1.79)
- Fido2 (>= 4.0.1)
- FluentMigrator (>= 8.0.1)
- Microsoft.Extensions.Configuration (>= 10.0.9)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Http (>= 10.0.9)
- Microsoft.Extensions.Logging (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options (>= 10.0.9)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.19.1)
- Microsoft.IdentityModel.Logging (>= 8.19.1)
- Microsoft.IdentityModel.Tokens (>= 8.19.1)
- QRCoder (>= 1.6.0)
- System.Formats.Cbor (>= 9.0.24)
- Themia.Challenges (>= 0.63.1)
- Themia.Totp (>= 0.63.1)
- Themia.WebAuthn (>= 0.63.1)
-
net8.0
- Dapper (>= 2.1.79)
- Fido2 (>= 4.0.1)
- FluentMigrator (>= 8.0.1)
- Microsoft.Extensions.Configuration (>= 10.0.9)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Http (>= 10.0.9)
- Microsoft.Extensions.Logging (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options (>= 10.0.9)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.19.1)
- Microsoft.IdentityModel.Logging (>= 8.19.1)
- Microsoft.IdentityModel.Tokens (>= 8.19.1)
- QRCoder (>= 1.6.0)
- System.Formats.Cbor (>= 9.0.24)
- Themia.Challenges (>= 0.63.1)
- Themia.Totp (>= 0.63.1)
- Themia.WebAuthn (>= 0.63.1)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Themia.SecondFactor:
| Package | Downloads |
|---|---|
|
Themia.Modules.Identity.ExternalAuth.AspNetCore
External OAuth/OIDC authentication for Themia Identity — server-side code→token exchange, PKCE, token-bound nonce, id-token validation, JWKS RS256 + HS256, and the external-login flow over a bring-your-own IExternalLoginService. No EF/Dapper, no user store. |
|
|
Themia.SecondFactor.PostgreSql
PostgreSQL (15+) Dapper store + FluentMigrator runner for Themia.SecondFactor. |
|
|
Themia.Modules.Identity.SecondFactor
Bridge between Themia.SecondFactor and Themia Identity — the second-factor policy over the multi-method service, verify-then-complete for pending logins, per-session proof reads and the restriction lift after a strong enrolment. |
GitHub repositories
This package is not used by any popular GitHub repositories.