AuthIMO 0.1.1
dotnet add package AuthIMO --version 0.1.1
NuGet\Install-Package AuthIMO -Version 0.1.1
<PackageReference Include="AuthIMO" Version="0.1.1" />
<PackageVersion Include="AuthIMO" Version="0.1.1" />
<PackageReference Include="AuthIMO" />
paket add AuthIMO --version 0.1.1
#r "nuget: AuthIMO, 0.1.1"
#:package AuthIMO@0.1.1
#addin nuget:?package=AuthIMO&version=0.1.1
#tool nuget:?package=AuthIMO&version=0.1.1
AuthIMO
AuthIMO is a Windows-first authenticator app + a cross-platform .NET library for MFA codes only:
- TOTP (RFC 6238) and HOTP (RFC 4226)
- Backup codes (stored in the encrypted vault)
- QR code scanning (screen capture + decode via CodeGlyphX, our first-party QR library)
Not supported (by design): push approvals, SMS delivery, email delivery.
Application preview
Current codes, timing, and account actions in the Windows app. All accounts shown are fictional. See the capture guide for matching light, dark, setup, recovery, and compact views.
Saving account changes
FileVaultStorage records the revision read by Load() or written by a successful Save().
It rejects a stale save with VaultStorageConflictException when another instance changed or
removed the file. A new storage instance can create a missing vault; it must load an existing
vault before updating it. Writers coordinate using a persistent empty <vault>.lock file.
Use VaultAccountStore with the opened VaultDocument and that same storage instance to add
account batches, remove accounts, apply detached account edits, or consume HOTP codes. It restores the in-memory account
state when persistence fails. ConsumeHotp() saves the next counter before returning the
current code. If copying that code to the clipboard fails afterward, the counter remains
consumed; the next attempt uses a new code.
After a conflict, reopen the storage and vault document, then retry the intended action. Do not reload only the storage revision while retaining an older document. The caller owns the document lifetime and serializes access to the account store and its mutable models.
Architecture Overview
graph TB
subgraph WPF["AuthIMO.App (Windows)"]
subgraph Windows["Windows"]
MW[MainWindow<br/>Advanced Mode]:::window
MinW[MinimalWindow<br/>Compact Mode]:::window
OW[OnboardingWindow<br/>Wizard]:::window
Tray[System Tray]:::window
end
subgraph ViewModels["ViewModels (MVVM)"]
AVM[AccountsViewModel]:::viewmodel
AddVM[AddAccountViewModel]:::viewmodel
MinVM[MinimalOtpViewModel]:::viewmodel
OnbVM[OnboardingViewModel]:::viewmodel
SetVM[SettingsViewModel]:::viewmodel
VaultVM[VaultSecurityViewModel]:::viewmodel
end
subgraph Services["Services"]
VS[VaultSession]:::service
QRS[QrScanService]:::service
CS[ClipboardService]:::service
ALS[AutoLockService]:::service
TS[ThemeService]:::service
end
CGX[CodeGlyphX<br/>QR Library<br/>first-party]:::firstparty
end
subgraph Core["AuthIMO (Cross-platform .NET 8.0)"]
subgraph OTP["OTP Generation"]
TOTP[TOTP<br/>RFC 6238]:::otp
HOTP[HOTP<br/>RFC 4226]:::otp
Verifiers[Verifiers<br/>Replay Guard]:::otp
Parser[OtpAuthParser]:::otp
end
subgraph Vault["Vault & Encryption"]
VF[VaultFile<br/>Binary I/O]:::vault
VD[VaultDocument<br/>JSON Model]:::vault
AES[AES-256-GCM]:::crypto
end
subgraph KeyProt["Key Protection"]
PBKDF2[PBKDF2-SHA256<br/>Portable]:::crypto
DPAPI[DPAPI<br/>Windows]:::crypto
TPM[TPM<br/>Platform Crypto Provider]:::crypto
end
subgraph Import["Import / Export"]
GA[Google Auth<br/>Migration]:::import
PSKC[PSKC<br/>RFC 6030]:::import
Entra[Entra OATH<br/>CSV]:::import
end
end
MW --> AVM
MinW --> MinVM
OW --> OnbVM
AVM --> VS
AddVM --> QRS
MinVM --> QRS
OnbVM --> QRS
QRS --> CGX
VS --> VF
VF --> AES
AES --> PBKDF2
AES --> DPAPI
AES --> TPM
classDef window fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:#01579b
classDef viewmodel fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#7b1fa2
classDef service fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#e65100
classDef firstparty fill:#e8eaf6,stroke:#3949ab,stroke-width:2px,color:#1a237e
classDef otp fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#2e7d32
classDef vault fill:#fff8e1,stroke:#f57f17,stroke-width:2px,color:#f57f17
classDef crypto fill:#ffebee,stroke:#c62828,stroke-width:2px,color:#c62828
classDef import fill:#e0f2f1,stroke:#00695c,stroke-width:2px,color:#00695c
Security Architecture
flowchart TB
subgraph Unlock["๐ Vault Unlock"]
direction TB
subgraph PathA["Path A: Passphrase (Portable)"]
Pass[๐ User Passphrase]:::input
PBKDF[PBKDF2-SHA256<br/>600k iterations + salt]:::crypto
DK[Derived Key<br/>32 bytes]:::key
Unwrap1[AES-GCM Unwrap]:::crypto
end
subgraph PathB["Path B: Windows Convenience"]
DPBlob[๐ช Platform Protected Blob<br/>DPAPI or TPM]:::windows
DPUnwrap[Platform Unwrap<br/>DPAPI or TPM]:::windows
end
MK[๐ Master Key<br/>random 32 bytes<br/>in memory]:::masterkey
end
subgraph Decrypt["๐ Vault Decryption"]
AES[๐ก๏ธ AES-256-GCM<br/>with AAD]:::crypto
JSON[๐ Decrypted Vault JSON<br/>โข accounts<br/>โข backupCodes<br/>โข metadata]:::data
end
Pass --> PBKDF
PBKDF --> DK
DK --> Unwrap1
Unwrap1 --> MK
DPBlob --> DPUnwrap
DPUnwrap --> MK
MK --> AES
AES --> JSON
classDef input fill:#bbdefb,stroke:#1565c0,stroke-width:2px,color:#0d47a1
classDef crypto fill:#ffcdd2,stroke:#c62828,stroke-width:2px,color:#b71c1c
classDef key fill:#fff9c4,stroke:#f9a825,stroke-width:2px,color:#f57f17
classDef windows fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px,color:#1b5e20
classDef masterkey fill:#ff8a65,stroke:#d84315,stroke-width:3px,color:#bf360c
classDef data fill:#b2dfdb,stroke:#00695c,stroke-width:2px,color:#004d40
QR Scanning Flow
flowchart LR
subgraph Input["๐ User Action"]
Scan[๐บ Scan All Screens]:::action
Region[โ๏ธ Pick Region]:::action
end
subgraph Capture["๐ท Screen Capture"]
SC[ScreenCapture<br/>GDI+ BitBlt]:::capture
end
subgraph Decode["๐ CodeGlyphX (first-party)"]
QRD[QrDecoder.TryDecodeAll<br/>โข Balanced/Robust profiles<br/>โข Time budget 1.5-2.5s<br/>โข MaxDimension scaling<br/>โข AutoCrop, TileScan]:::decoder
end
subgraph Filter["๐ท๏ธ Payload Filter"]
OTP[otpauth://]:::supported
Migration[otpauth-migration://]:::supported
Phone[phonefactor://]:::rejected
end
subgraph Result["โ
Result"]
Add[โ Parse & Add<br/>Account]:::success
Multi[โ Extract All<br/>Accounts]:::success
Reject[โ REJECTED<br/>Use TOTP setup]:::error
end
Scan --> SC
Region --> SC
SC --> QRD
QRD --> OTP
QRD --> Migration
QRD --> Phone
OTP --> Add
Migration --> Multi
Phone --> Reject
classDef action fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#0d47a1
classDef capture fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#4a148c
classDef decoder fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,color:#e65100
classDef supported fill:#c8e6c9,stroke:#388e3c,stroke-width:2px,color:#1b5e20
classDef rejected fill:#ffcdd2,stroke:#d32f2f,stroke-width:2px,color:#b71c1c
classDef success fill:#a5d6a7,stroke:#2e7d32,stroke-width:3px,color:#1b5e20
classDef error fill:#ef9a9a,stroke:#c62828,stroke-width:3px,color:#b71c1c
Component Structure
graph LR
subgraph VM["๐ฆ ViewModels"]
Shell[ShellViewModel]:::vm
Accounts[AccountsViewModel]:::vm
AddAcc[AddAccountViewModel]:::vm
Minimal[MinimalOtpViewModel]:::vm
Onboard[OnboardingViewModel]:::vm
Settings[SettingsViewModel]:::vm
VaultSec[VaultSecurityViewModel]:::vm
end
subgraph Views["๐ผ๏ธ Views"]
MainWin[MainWindow]:::view
AccView[AccountsView]:::view
AddView[AddAccountView]:::view
MinView[MinimalWindow]:::view
OnbView[OnboardingWindow]:::view
SetView[SettingsView]:::view
VaultView[VaultSecurityView]:::view
end
subgraph Svc["โ๏ธ Services"]
VSess[VaultSession<br/>unlock/lock/save]:::svc
QrSvc[QrScanService<br/>screen capture]:::svc
ClipSvc[ClipboardService<br/>auto-clear]:::svc
AutoLock[AutoLockService<br/>idle detection]:::svc
Theme[ThemeService<br/>light/dark/auto]:::svc
OnbSvc[OnboardingService<br/>JSON config]:::svc
end
Shell --> MainWin
Accounts --> AccView
AddAcc --> AddView
Minimal --> MinView
Onboard --> OnbView
Settings --> SetView
VaultSec --> VaultView
Accounts --> VSess
Accounts --> ClipSvc
AddAcc --> QrSvc
Minimal --> QrSvc
Onboard --> QrSvc
Settings --> AutoLock
Settings --> Theme
Onboard --> OnbSvc
classDef vm fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px,color:#1a237e
classDef view fill:#e0f7fa,stroke:#0097a7,stroke-width:2px,color:#006064
classDef svc fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:#880e4f
Repository layout
| Project | Target | Description |
|---|---|---|
src/AuthIMO |
net8.0 / net8.0-windows |
Cross-platform OTP library with Windows extras |
src/AuthIMO.App |
net8.0-windows |
Windows WPF application |
tests/AuthIMO.Tests |
net8.0 |
Unit tests |
AuthIMO Features
- Encoding: Base32 encode/decode
- OTP: HOTP/TOTP (SHA1/SHA256/SHA512, 6/8 digits, 30/60s)
- Verification: TOTP window validation + replay guard; HOTP look-ahead + resync
- Parsing:
otpauth://URI parsing/validation - Import: Bulk text import, Google Authenticator
otpauth-migration:// - Export: PSKC (RFC 6030), Entra OATH CSV
- Vault: Encrypted vault format (AES-GCM + PBKDF2)
- Audit:
VaultAuditdetects misconfigurations - Windows: DPAPI + TPM key protectors (Platform Crypto Provider), default paths under
%APPDATA%\AuthIMO\
AuthIMO.App Features
- Dual mode: Advanced (full navigation) and Minimal (compact single-window)
- QR scanning: Screen capture + CodeGlyphX decode (first-party library)
- Onboarding: JSON-driven wizard with multi-language support
- System tray: Quick actions and notifications
- Theming: Light, Dark, Auto (follows Windows)
Security model
| Aspect | Description |
|---|---|
| Storage | Secrets never stored plaintext on disk |
| Encryption | Vault always encrypted with AES-256-GCM |
| Portable mode | Passphrase โ PBKDF2-SHA256 (600k iterations) โ Master Key |
| Windows mode | DPAPI or TPM-protected Master Key (bound to Windows user/device) |
| In memory | Secrets exist in memory only while unlocked |
Code access policy (Phase 1)
- Optional PIN gate for revealing or copying OTP codes (Windows-only).
- Optional PIN gate for unlocking the vault (Windows-only).
- PIN verifier is stored as a DPAPI-protected payload in user settings.
- PIN unlock is time-bound (auto-lock after the configured timeout).
- Failed PIN retry backoff is persisted across restarts.
- If lockout-state persistence fails, AuthIMO emits an audit warning event when audit logging is enabled.
- When locked, OTP codes are not computed or copied.
Phase 2 (TPM) โ implemented
- TPM-backed master key protection via the Windows Platform Crypto Provider.
- Optional Windows Hello/PIN UI enforcement when creating TPM keys.
- DPAPI remains available as a fallback when allowed by policy.
- PCR binding is supported via policy (
RequirePcrBinding, optionalTpmPcrMask/TpmPcrAlgorithmId).
Enterprise Policy (registry + config)
Policy is loaded in this order:
- Registry (
HKLM\Software\Policies\EvotecServices\AuthIMO) authimo.policy.jsonnext to the executable%APPDATA%\AuthIMO\authimo.policy.json
If the registry policy is present, it is authoritative unless AllowPolicyOverride=1 is set.
If a policy file is present but unreadable or malformed, startup fails closed.
Policy keys
See assets/policies/AuthIMO.policy.enterprise-lockdown.json for a secure-by-default enterprise template.
AllowPassphrase(bool)AllowDpapi(bool)AllowTpm(bool)RequireTpm(bool)RequireHello(bool, TPM only)RequireHelloForUnlock(bool)RequireHelloForCodes(bool)AllowHelloInsteadOfPin(bool)RequirePcrBinding(bool, TPM only)TpmPcrMask(uint, TPM only; optional PCR bitmask)TpmPcrAlgorithmId(uint, TPM only; optional ALG_ID for PCR bank)RequirePassphraseRecovery(bool)RequirePinForUnlock(bool)RequirePinForCodes(bool)AllowOtpAuth(bool)AllowOtpAuthMigration(bool)AllowEntraCsv(bool)WarnOnSecretReveal(bool)AllowVaultExport(bool)AuditLogEnabled(bool)AuditLogTarget(string: File, EventLog, Both)AuditLogPath(string)AuditEventLogName(string)AuditEventLogSource(string)AllowedUserRegex(string)AllowedPuidRegex(string)BlockPrivilegedAccounts(bool)AllowedIssuers(string[] or delimited string)AllowedEmailDomains(string[] or delimited string)AllowedTenantIds(string[] or delimited string)- Tenant checks use explicit account
tenantIdwhen present and also parse common otpauth keys (tid,tenantid,tenant_id).
- Tenant checks use explicit account
MinPassphraseLength(int)PassphraseRegex(string)PinMinLength(int)PinMaxLength(int)PinRegex(string)RequireAutoLockAfterSeconds(int)MaxAutoLockAfterSeconds(int)ForceOnboarding(bool)DefaultVaultPath(string)ForceVaultPath(bool)AutoCreateVault(bool)ForceCreateNewVault(bool)ForceMinimalMode(bool)
Example authimo.policy.json:
{
"AllowPassphrase": true,
"AllowDpapi": false,
"AllowTpm": true,
"RequireTpm": true,
"RequireHello": true,
"RequirePinForUnlock": true,
"RequirePinForCodes": true,
"RequirePassphraseRecovery": true,
"AllowedEmailDomains": ["contoso.com"]
}
Threat model
flowchart LR
subgraph Protected["๐ก๏ธ Protects Against"]
P1[๐ Copying vault file<br/>without credentials]:::protected
P2[๐พ Plaintext storage<br/>on disk]:::protected
P3[๐ Tampering<br/>AES-GCM auth]:::protected
end
subgraph NotProtected["โ ๏ธ Does NOT Protect Against"]
N1[๐ฆ Compromised<br/>OS/user session]:::notprotected
N2[โจ๏ธ Keylogger<br/>capturing passphrase]:::notprotected
N3[๐ค Malicious admin<br/>with full control]:::notprotected
N4[๐ง Memory reading<br/>while unlocked]:::notprotected
end
classDef protected fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px,color:#1b5e20
classDef notprotected fill:#ffcdd2,stroke:#c62828,stroke-width:2px,color:#b71c1c
Operational guidance:
- Prefer strong passphrase in portable mode
- Windows convenience mode binds vault to your Windows user via DPAPI or TPM
- Treat provisioning exports (PSKC / Entra CSV /
otpauth://URIs) as sensitive
QR Code Scanning
| Format | Support | Notes |
|---|---|---|
otpauth://totp/... |
โ Supported | Standard TOTP |
otpauth://hotp/... |
โ Supported | Standard HOTP |
otpauth-migration://... |
โ Supported | Google Authenticator export |
phonefactor://... |
โ Rejected | Microsoft Authenticator push (proprietary) |
Decode options (tuned for screen capture):
- Time budget: 1.5-2.5 seconds
- MaxDimension downscaling for large displays
- AutoCrop and TileScan for finding QR codes
Using the WPF app
Minimal Mode (default)
Compact single-window view for quick code access:
- Single account display with large code
- Navigation arrows to switch accounts
- Quick add via QR scan or manual entry
- Stay-on-top option
- Switch to Advanced mode via menu
Advanced Mode
Full-featured interface with navigation:
- Unlock: Create new vault or open existing (passphrase / DPAPI / TPM / hybrid)
- Add accounts: Scan QR, paste
otpauth://URIs, or manual entry - Use codes: Copy to clipboard (auto-clear configurable), privacy mode
- Backup codes: Add, generate, copy, remove
- Vault security: Passphrase, DPAPI/TPM toggle, key rotation, export
Keyboard shortcuts: Ctrl+N Add, Ctrl+B Backup, Ctrl+, Settings, Ctrl+Shift+V Vault, Ctrl+L Lock
OTP Algorithms
| Setting | Options | Default |
|---|---|---|
| Type | TOTP, HOTP | TOTP |
| Digits | 6, 8 | 6 |
| Period | 30s, 60s | 30s |
| Algorithm | SHA1, SHA256, SHA512 | SHA1 |
Why SHA1 default? Most providers use HMAC-SHA1. The SHA-1 collision attacks target plain hashing, not HMAC-SHA1 in OTP context. Use SHA256/SHA512 only when provider requires it.
Vault file format (v2)
block-beta
columns 1
block:Header["๐ Header"]
Magic["Magic: AUTHIMO\\0 (8 bytes)"]
Version["Version: UInt16"]
Flags["Flags: UInt16 (1=Pass, 2=Platform)"]
end
block:PassBlock["๐ If Passphrase flag"]
KDF["KDF: UInt16 + Iterations: UInt32"]
Salt["Salt: length + bytes"]
WrapNonce["Wrap Nonce: 12 bytes"]
WrapCipher["Wrap Ciphertext: 32 bytes"]
WrapTag["Wrap Tag: 16 bytes"]
end
block:DPAPIBlock["๐ช If Platform flag (DPAPI/TPM)"]
DPBlob["Protected Blob: length + bytes"]
end
block:Payload["๐ Encrypted Payload"]
PayNonce["Payload Nonce: 12 bytes"]
PayCipher["Encrypted Payload: JSON"]
PayTag["Payload Tag: 16 bytes"]
end
style Header fill:#e3f2fd,stroke:#1976d2
style PassBlock fill:#fff3e0,stroke:#f57c00
style DPAPIBlock fill:#e8f5e9,stroke:#388e3c
style Payload fill:#fce4ec,stroke:#c2185b
Payload (AES-GCM encrypted JSON):
{ "metadata": {...}, "accounts": [...], "backupCodes": [...] }
AAD binding: Header prefix binds to ciphertext for tamper detection.
Platform protected blob: DPAPI uses the raw protected payload (legacy). TPM uses an ATPM header (v1) with key name + flags + wrapped master key.
Onboarding Wizard
Enterprise-friendly wizard with:
- JSON configuration (
%APPDATA%\AuthIMO\onboarding.json) - Multi-language support with fallback
- Embedded QR scanning within wizard steps
- Custom branding (logo, company name)
Development
# Build
dotnet build AuthIMO.sln -c Release
# Test
dotnet test AuthIMO.sln -c Release
# Run
dotnet run --project src/AuthIMO.App/AuthIMO.App.csproj
Dependencies
| Package | Purpose |
|---|---|
| CodeGlyphX | QR code encoding/decoding (first-party) |
| System.Security.Cryptography | AES-GCM, PBKDF2 |
| System.Security.Cryptography.ProtectedData | DPAPI (Windows) |
Project References
graph TD
WPF[๐ฅ๏ธ AuthIMO.App]:::wpf --> Core[๐ AuthIMO]:::core
WPF --> CGX[๐ท CodeGlyphX<br/>first-party]:::firstparty
Tests[๐งช AuthIMO.Tests]:::tests --> Core
classDef wpf fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#0d47a1
classDef core fill:#e8f5e9,stroke:#388e3c,stroke-width:2px,color:#1b5e20
classDef firstparty fill:#e8eaf6,stroke:#3949ab,stroke-width:2px,color:#1a237e
classDef tests fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#4a148c
| 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. net8.0-windows7.0 is compatible. 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
- No dependencies.
-
net8.0-windows7.0
- System.Security.Cryptography.ProtectedData (>= 10.0.12)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.1 | 143 | 9/30/2026 |