Omni2FA.Core
0.7.2
dotnet add package Omni2FA.Core --version 0.7.2
NuGet\Install-Package Omni2FA.Core -Version 0.7.2
<PackageReference Include="Omni2FA.Core" Version="0.7.2" />
<PackageVersion Include="Omni2FA.Core" Version="0.7.2" />
<PackageReference Include="Omni2FA.Core" />
paket add Omni2FA.Core --version 0.7.2
#r "nuget: Omni2FA.Core, 0.7.2"
#:package Omni2FA.Core@0.7.2
#addin nuget:?package=Omni2FA.Core&version=0.7.2
#tool nuget:?package=Omni2FA.Core&version=0.7.2
Omni2FA
Drop-in multi-method two-factor authentication for self-hosted apps — TOTP, Email OTP, WebAuthn/passkeys, recovery codes — behind one OpenAPI contract.
You verify the password and mint your session; Omni2FA handles everything 2FA in between — enrollment, the login challenge, OTP/WebAuthn ceremonies, recovery codes, persistence, rate limiting, audit. Backend service + endpoints + EF store on .NET; headless hooks on React. Other stacks implement the same contract (roadmap below).
For apps that own their user table (ASP.NET Core Identity, custom JWT/cookie auth, …). Not for managed identity providers (Auth0, Clerk, Cognito, Firebase, Supabase, Okta, WorkOS) — they already ship 2FA.
Packages
| Stack | Install | Notes |
|---|---|---|
| .NET (ASP.NET Core) | Omni2FA.AspNetCore + Omni2FA.AspNetCore.EntityFrameworkCore |
Omni2FA.Core comes transitively |
| React | @omni2fa/core + @omni2fa/react |
hooks; styled MUI dialogs (@omni2fa/react-mui) — planned |
Other backends (Node, Python) and frontends (Angular, Vue) are on the roadmap; any stack can implement the OpenAPI contract.
Backend — ASP.NET Core
dotnet add package Omni2FA.AspNetCore
dotnet add package Omni2FA.AspNetCore.EntityFrameworkCore
Program.cs
builder.Services.AddOmni2Fa(o => builder.Configuration.GetSection("Omni2Fa").Bind(o));
builder.Services.AddOmni2FaEntityFrameworkStore<AppDbContext>(); // or implement the store interfaces yourself
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapOmni2Fa(); // mounts /api/2fa/*
Your DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyOmni2FaConfiguration(); // 2FA tables (names configurable)
}
Hook the pre-auth token into your existing login — the seam between your password check and the 2FA step:
public class AuthService(IPreAuthTokenIssuer preAuth, ITwoFactorMethodStore methods) {
// after you verify the password:
if (await methods.HasActiveMethodsAsync(userId)) {
var ticket = preAuth.Issue(userId); // short-lived JWT
var available = await methods.ListActiveByUserAsync(userId);
return Challenge(ticket.Token, ticket.ExpiresAt, available); // your DTO → frontend
}
// else: mint your session as usual
// when the frontend finishes 2FA, it calls your "finalize" with the verifiedToken from the verify response:
var verifiedUserId = preAuth.ValidateVerified(verifiedToken); // null if not actually verified → reject
}
Use the DTOs the library already ships at the host seam — don't reinvent them (all in Omni2FA.Core.Dtos / .Services):
| DTO | Where you use it |
|---|---|
PreAuthTokenInfo |
returned by Issue / IssueVerified — { Token, ExpiresAt } |
PreAuthChallengeResponse |
your login response when 2FA is required — { PreAuthToken, AvailableMethods, ExpiresAt } |
TwoFactorMethodDto |
shape of an enrolled method (AvailableMethods items) |
VerifySuccessResponse |
the /challenge/verify body — { Verified, UserId, VerifiedToken, ExpiresAt } |
ErrorResponse + Omni2FaErrorCodes |
error envelope and the stable error-code constants |
appsettings.json
"Omni2Fa": {
"PreAuth": { "SigningKey": "<32+ char HMAC key — from env/secrets>" },
"Totp": { "Issuer": "MyApp" },
"Email": { "FromAddress": "no-reply@myapp.com",
"Smtp": { "Host": "smtp.example.com", "Port": 587, "Username": "...", "Password": "...", "UseStartTls": true } },
"WebAuthn": { "RelyingPartyId": "myapp.com", "Origins": [ "https://myapp.com" ] }
}
That's the whole backend: endpoints, the three methods, recovery codes, rate limiting and audit are live.
Frontend — React
npm i @omni2fa/core @omni2fa/react
Once, at the app root
import { createOmni2Fa } from '@omni2fa/core';
import { Omni2FaProvider } from '@omni2fa/react';
export const omni = createOmni2Fa({ baseUrl: '/api/2fa' });
// after your login, hand the client your host session token (or use cookies: createOmni2Fa({ credentials: 'include' })):
omni.client.setSessionToken(sessionToken);
<Omni2FaProvider value={omni}>{children}</Omni2FaProvider>
The hooks expose state + actions; you render the UI (headless).
Login challenge
import { useChallenge } from '@omni2fa/react';
const { status, context, pick, submit, useRecoveryCode } = useChallenge();
// pick(methodId) → submit(code) (WebAuthn auto-runs the browser ceremony)
// status: 'idle' | 'awaitingCode' | 'asserting' | 'verifying' | 'verified' | 'failed' | …
// on 'verified': send context.verifiedToken to your finalize endpoint (not the pre-auth token)
Hooks: useMethods, useTotpEnrollment, useEmailEnrollment, useWebAuthnEnrollment, useChallenge (+ *Selector variants). A full headless UI you can copy lives in examples/full/frontend. Styled drop-in components (@omni2fa/react-mui) are planned.
Configuration (key options, under Omni2Fa)
| Option | Default | Purpose |
|---|---|---|
PreAuth.SigningKey |
— (required, ≥32 chars) | HMAC key for the pre-auth ticket; validated at startup |
PreAuth.Ttl |
5 min | Pre-auth ticket lifetime |
PreAuth.VerifiedTtl |
2 min | Verified-handoff token lifetime (the finalize proof) |
Totp.Issuer |
Omni2FA |
Name shown in authenticator apps |
Email.Smtp.* / Email.BackgroundDelivery |
— / true |
SMTP transport; codes sent on a background worker by default |
WebAuthn.RelyingPartyId / Origins |
localhost / http://localhost:5173 |
Must match your real hostname (HTTPS off-localhost) |
WebAuthn.MaxCredentialsPerUser |
3 | Passkey cap per user |
RateLimit.{PermitLimit,Window} |
20 / 1 min | Per-IP limit on verify/enroll endpoints |
AspNetCore.AllowDisablingLastMethod |
true |
Set false to forbid removing the last method |
| EF table/column names | Omni2Fa* |
Override via ApplyOmni2FaConfiguration(o => …) for migrations |
Extension points (swap any piece — TryAdd, host wins)
| Interface | Replace to… |
|---|---|
IEmailSender |
send via your own infra (SendGrid/SES/relay) instead of MailKit/SMTP |
IEmailMessageBuilder |
customize/localize the OTP email copy |
IOmni2FaAuditSink |
forward audit events to your log/SIEM (default → ILogger) |
ITwoFactorMethodStore / ITwoFactorChallengeStore / IRecoveryCodeStore |
use Mongo/Dapper/raw ADO instead of EF Core |
IUserContextAccessor |
derive the current user id from a custom claim/header |
IPreAuthTokenIssuer |
change how the pre-auth ticket is minted/validated |
IWebAuthnCeremonyService |
swap the FIDO2 implementation |
Methods & status
| Method | State |
|---|---|
| TOTP (authenticator apps) | ✅ |
| Email OTP (SMTP) | ✅ |
| WebAuthn (passkeys / security keys) | ✅ |
| Recovery codes (one-time, hashed) | ✅ |
| Rate limiting · audit sink | ✅ |
🚧 Status — pre-1.0 (0.6.x). Functionally complete for .NET + React, verified by a live end-to-end run, but young: no automated test suite yet (planned for v1.0), and WebAuthn/email not yet battle-tested across many environments. Suitable for your own apps; harden before betting a production product on it.
More
examples/full— runnable ASP.NET + React + SQLite app with all methods.docs/ARCHITECTURE.md·docs/FLOWS.md·docs/ROADMAP.md·docs/PUBLISHING.mdCore/protocol/omni2fa.openapi.yaml— the cross-stack contract.
License
MIT — see LICENSE.
| 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 was computed. 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. |
-
net8.0
- Microsoft.AspNetCore.DataProtection.Abstractions (>= 8.0.10)
- Microsoft.Extensions.Options (>= 8.0.2)
- Microsoft.IdentityModel.Tokens (>= 8.2.1)
- Otp.NET (>= 1.4.0)
- System.IdentityModel.Tokens.Jwt (>= 8.2.1)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Omni2FA.Core:
| Package | Downloads |
|---|---|
|
Omni2FA.AspNetCore
Omni2FA — ASP.NET Core integration. Endpoints (/api/2fa/*), DI extensions (AddOmni2Fa), filters, default SMTP email sender. Drop-in 2FA for ASP.NET Core apps. |
|
|
Omni2FA.AspNetCore.EntityFrameworkCore
Omni2FA — Entity Framework Core store adapter. Implements ITwoFactorMethodStore and ITwoFactorChallengeStore on EF Core. Works with any relational provider (PostgreSQL, SQL Server, SQLite, MySQL, Oracle). Plug into your existing DbContext via modelBuilder.ApplyOmni2FaConfiguration(). |
GitHub repositories
This package is not used by any popular GitHub repositories.