AuthIMO 0.1.1

dotnet add package AuthIMO --version 0.1.1
                    
NuGet\Install-Package AuthIMO -Version 0.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AuthIMO" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AuthIMO" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="AuthIMO" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AuthIMO --version 0.1.1
                    
#r "nuget: AuthIMO, 0.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AuthIMO@0.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AuthIMO&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=AuthIMO&version=0.1.1
                    
Install as a Cake Tool

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

AuthIMO account list in light mode

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: VaultAudit detects 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, optional TpmPcrMask/TpmPcrAlgorithmId).

Enterprise Policy (registry + config)

Policy is loaded in this order:

  1. Registry (HKLM\Software\Policies\EvotecServices\AuthIMO)
  2. authimo.policy.json next to the executable
  3. %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 tenantId when present and also parse common otpauth keys (tid, tenantid, tenant_id).
  • 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:

  1. Unlock: Create new vault or open existing (passphrase / DPAPI / TPM / hybrid)
  2. Add accounts: Scan QR, paste otpauth:// URIs, or manual entry
  3. Use codes: Copy to clipboard (auto-clear configurable), privacy mode
  4. Backup codes: Add, generate, copy, remove
  5. 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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 146 9/30/2026