Skip to main content
All Projects

2-FA Made for Digital Twin

Two-factor authentication system for a Unity-based application. This repo was made for our digital twin system but can be used for anything with slight changes. It also uses free services.

C#ASP.NET CoreUnityPostgreSQLRedisJWTWebAuthn2-FASecurityMailKit

Overview

A complete two-factor authentication system built for a Unity-based desktop application, originally designed as the auth layer for the Digital Twin project. While purpose-built for that use case, it is architected to be reusable with minimal changes in any Unity application that needs a full auth backend.

The system covers the entire authentication lifecycle: signup, email-based 6-digit 2FA codes, password recovery, account management, JWT session tokens, Redis-backed brute-force protection, role-based admin endpoints, and full WebAuthn/FIDO2 passkey support — all backed by a 65+ test suite.

Tech Stack

  • ASP.NET Core 8 / C# — REST API with 9 auth endpoints, 4 passkey endpoints, 2 admin endpoints, and a health check.
  • PostgreSQL — User records, 2FA codes, password reset tokens, and passkey credentials via EF Core + Npgsql.
  • Redis — Brute-force rate limiting on both /auth/login and /auth/verify-2fa via StackExchange.Redis.
  • JWT — HS256 tokens issued on successful 2FA verification or passkey assertion; configurable expiry.
  • WebAuthn / FIDO2 — Full passkey registration and assertion via Fido2NetLib. Passkey-enabled users skip the email/2FA flow entirely.
  • MailKit / Brevo — 2FA code and password reset email delivery via Brevo's free SMTP relay.
  • BCrypt — Password hashing at work factor 12.
  • Unity / C# — MonoBehaviour frontend using UnityWebRequest coroutines; the HTTP client singleton (ApiClient) never blocks the Unity main thread.
  • xUnit / Moq — 65+ tests across integration and unit scenarios.

Authentication Flow

  1. User submits email and password to /auth/login.
  2. If credentials are valid and the account is not passkey-enabled, a 6-digit code is emailed.
  3. User submits the code to /auth/verify-2fa, which validates it and returns a signed JWT.
  4. The Unity client stores the JWT via ApiClient.SetAuthToken() and injects it as a Bearer header on all subsequent requests.

Passkey-enabled users skip steps 2 and 3 entirely — they complete a WebAuthn assertion challenge (/auth/passkey/assert-options/auth/passkey/assert-complete) and receive the same JWT on success.

Key Challenges

Redis-backed brute-force protection: Rate limiting is applied independently to the login endpoint (5 failed attempts / 15-min window) and the verify-2fa endpoint (10 attempts / 10-min window), each with its own Redis key namespace. The verify-2fa counter resets on a successful verification so a legitimate user is not locked out after a single typo.

WebAuthn in a .NET backend: The full FIDO2 registration and assertion flow is implemented via Fido2NetLib. An InMemoryChallengeStore enforces nonce TTLs on assertion and registration challenges, and a ChallengeStoreCleanupService (an IHostedService) sweeps expired entries every 5 minutes to prevent memory growth.

HTTPS in Unity development: Unity's UnityWebRequest rejects the ASP.NET Core self-signed dev certificate by default. A DevCertificateHandler compiled only for UNITY_EDITOR and DEVELOPMENT_BUILD targets accepts the cert during development without affecting release builds — the certificate bypass is never present in production.

Password reset token security: Reset tokens are stored in the database as SHA-256 hex digests. The plain-text token is emailed to the user but never persisted. A compromised database cannot be used to replay reset tokens.

Test isolation: PostgreSQL is replaced with EF Core InMemory (unique Guid-named database per factory instance) in the CustomWebApplicationFactory. Redis is mocked via Moq. Every service with side effects has a dedicated fake (NoOpEmailService, AlwaysAllowRateLimitService, FakeFido2, FakeChallengeStore) injected at the factory level so tests are fully deterministic.

Outcomes

65+ tests passing across 14 test files covering HTTP integration, unit, and rate-limit scenarios. The system functions as a complete, production-grade drop-in auth layer for Unity applications and uses only free-tier services (Brevo SMTP, PostgreSQL, Redis).