Skip to main content

Multi-Modal Identity & Authentication

Status: PLANNED (V1.0–V1.3) Effort: XL (6-8 weeks, spread across releases) Dependencies: Auth (done), SSO Login (done) Priority: HIGH — Enables families with kids, accessibility


The Story: Sophie Logs In​

Sophie is 8 years old. She has no email address, no password, no phone. She walks up to the family tablet and says:

"Morphee, it's me!"

Her voice is recognized. She's in. Her Space opens with her tasks, her conversations, her canvas — exactly where she left off. She creates a task ("finish math homework"), asks Morphee a question, and logs out by walking away.

Her dad Sebastien has email + 2FA. Her grandmother has face recognition because she forgets passwords. Her au-pair has a temporary voice profile that expires in 3 months.

Everyone uses the same Morphee. Everyone authenticates differently. That's multi-modal identity.


Core Principle: Identity =/= Email​

Traditional:
User = Email + Password → Access

Morphee:
Identity (one person)
├── Auth Method 1: Voice ("Morphee, it's me")
├── Auth Method 2: Face (smile at camera)
├── Auth Method 3: Email + Password
├── Auth Method 4: GitHub OAuth
├── Auth Method 5: Passkey (YubiKey)
└── Auth Level → ACL decisions

One identity, many ways to prove you're you. ACL actions can require specific methods or combinations:

ActionRequiredWhy
Create a taskAny method (level 1+)Low risk
Change group settings2FA or biometric (level 2+)Medium risk
Delete the groupParent/admin + 2FA (level 3)High risk
Kid invites a friendParent approvalParental control

Architecture​

Authentication Levels​

LevelMeaningExample
1Single factorEmail/password, OAuth
2Biometric or 2FAFace, voice, TOTP
3Multi-factor + biometricFace + TOTP, voice + hardware key

Step-Up Authentication​

When an action requires a higher level than the current session:

This works for any escalation — a developer using email+password who tries to access billing gets a TOTP challenge, not a rejection.

Identity Model​

class Identity(BaseModel):
id: str # UUID
display_name: str # "Sophie" — no email required
avatar_url: str | None
date_of_birth: date | None # For age-based ACL
is_minor: bool # Jurisdiction-aware (EU: 16, US: 13)
parent_identity_id: str | None # Link to parent for kids
groups: list[GroupMembership]

class AuthenticationMethod(BaseModel):
id: str
identity_id: str
method_type: Literal[
"email_password", "github_oauth", "google_oauth", "apple_oauth",
"face_recognition", "voice_recognition", "passkey",
"totp_2fa", "sms_2fa", "hardware_key"
]
primary: bool
verified: bool
biometric_template_id: str | None # Vault reference, NEVER raw data
last_used_at: datetime | None

class AuthenticationSession(BaseModel):
session_id: str
identity_id: str
methods_used: list[str] # ["voice_recognition"]
authentication_level: int # 1, 2, or 3
expires_at: datetime

Biometric Processing — Local Only​

All biometric processing happens on-device via Tauri Rust. No face or voice data ever leaves the device.

Face recognition: candle (FaceNet/ArcFace) → 512-dim embedding → cosine similarity > 0.85 Voice recognition: candle (ECAPA-TDNN) → speaker embedding → cosine similarity > 0.90 Liveness detection: eye blinks + head movement + texture analysis (face), speech naturalness + environment noise (voice)

Templates are stored in the VaultProvider (OS keychain on desktop, encrypted SQLite on Android). Only a template_id reference is stored in the database.

Connection to the Runtime Hierarchy​

Biometric models are excellent candidates for the compilation chain (see ROADMAP.md — Knowledge Pipeline):

ComponentRuntimeRationale
Face encoder (FaceNet)WasmRuntime (Level 3)Fixed model, performance-critical, runs on all platforms
Voice encoder (ECAPA)WasmRuntime (Level 3)Same — compiled, sandboxed, portable
Liveness detectionWasmRuntime (Level 3)Security-critical, must be tamper-resistant
Age estimation from facePythonRuntime (Level 1)Experimental, needs iteration
Custom passphrase logicLLMRuntime (Level 0)Flexible — "say something about your favorite animal"

Biometric WASM modules are not marketplace-shareable (they contain personal templates). But the model architecture could be shared — a community could publish better face encoders as WASM extensions.


Use Cases​

1. Kid Without Email (Primary Use Case)​

Parent creates Sophie's identity (no email needed):
→ Display name: "Sophie"
→ Date of birth: 2018-05-15
→ is_minor: true, parent_identity_id: <dad_id>

Parent enrolls Sophie's voice:
→ Sophie says "Morphee, it's me" 5 times
→ Voice template stored in vault (encrypted)

Daily use:
→ Sophie: "Morphee, it's me" → authenticated (level 2)
→ Creates tasks, chats, uses canvas → all allowed (level 1+ required)
→ Tries to change settings → parent approval required → dad notified

2. Multi-Method Adult​

Dad has: email+password (primary), GitHub OAuth, TOTP 2FA, face recognition

Quick morning login: face recognition → level 2 → full access
Sensitive action (delete group): level 3 required → step-up → enters TOTP → level 3
New device: no face enrolled → email+password → level 1 → adds TOTP → level 2

3. Accessibility: Voice-Only​

Maria (visually impaired) uses only voice:
→ "Morphee, it's me" → authenticated
→ All interactions via voice commands
→ Sensitive actions: voice + SMS code ("Morphee, my code is 123456")

4. Temporary Access (Au-pair, Contractor)​

Au-pair enrolled with voice, expiry date set (3 months)
→ Full access to family Space during contract
→ Auto-revoked on expiry
→ No residual biometric data (vault cleanup)

Security & Privacy​

Biometric Data Rules​

  1. NEVER store raw biometric data (images, audio) in the database
  2. ALWAYS store templates in VaultProvider (encrypted at rest)
  3. ALWAYS process biometrics locally (Tauri Rust, never cloud)
  4. GDPR: full biometric deletion on request (templates + auth methods)
  5. Consent: explicit opt-in for each biometric method, revocable anytime
  6. Liveness: required for all biometric auth (prevents photo/recording attacks)

Parent Controls for Minors​

  • Parent must authenticate to create/modify a minor's identity
  • Minor's authentication methods can only be managed by parent
  • Age-based ACL policies enforced automatically (jurisdiction-aware)
  • Parent approval requests for sensitive actions (with notification)
  • Parent can see activity log, revoke access instantly

Rollout Plan​

V1.0: Multi-Method Login (2 weeks)​

  • Identity + AuthenticationMethod models
  • Support email, GitHub/Google/Apple OAuth (leveraging existing SSO)
  • JWT with authentication_level claim
  • Frontend: multi-method login screen
  • Database migration: extend users → identities
  • Tests: multi-method authentication flows

V1.0: Parent-Kid Identity (2 weeks)​

  • Create minor identity (no email required)
  • Parent-child identity linking
  • Age-based ACL policies
  • Parent approval request system
  • Kid-friendly UI (larger buttons, simpler language)
  • Tests: parent-kid workflows

V1.3: Biometric Authentication (2 weeks)​

  • Tauri Rust: face encoding via candle (FaceNet/ArcFace)
  • Tauri Rust: voice encoding via candle (ECAPA-TDNN)
  • Vault integration for template storage
  • Frontend: FaceRecognitionAuth + VoiceRecognitionAuth components
  • Liveness detection (face + voice)
  • Tests: biometric enrollment + authentication

V1.3: Step-Up Authentication (1 week)​

  • Step-up challenge system
  • Authentication requirements per action/resource
  • Frontend: StepUpAuthDialog component
  • ACL integration
  • Tests: step-up flows

Future​

  • Passkeys (WebAuthn) — hardware security keys, OS biometric
  • Behavioral biometrics — typing patterns, usage patterns
  • Continuous authentication — re-verify periodically during session
  • Biometric fusion — combine face + voice for higher confidence
  • Age estimation — auto-detect minor status from face (PythonRuntime → WasmRuntime)

Database Schema​

-- Identities (extends existing users concept)
CREATE TABLE identities (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
display_name TEXT NOT NULL,
avatar_url TEXT,
date_of_birth DATE,
is_minor BOOLEAN DEFAULT FALSE,
parent_identity_id UUID REFERENCES identities(id) ON DELETE SET NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Authentication methods (multiple per identity)
CREATE TABLE authentication_methods (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
identity_id UUID NOT NULL REFERENCES identities(id) ON DELETE CASCADE,
method_type TEXT NOT NULL,
primary_method BOOLEAN DEFAULT FALSE,
verified BOOLEAN DEFAULT FALSE,
last_used_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
biometric_template_id TEXT, -- Vault reference only
oauth_provider_user_id TEXT,
oauth_email TEXT,
email TEXT,
email_verified BOOLEAN DEFAULT FALSE,
totp_secret_id TEXT, -- Vault reference
expires_at TIMESTAMP WITH TIME ZONE, -- For temporary access
UNIQUE(identity_id, method_type, email)
);

-- Step-up authentication challenges
CREATE TABLE step_up_challenges (
challenge_id UUID PRIMARY KEY,
identity_id UUID NOT NULL REFERENCES identities(id) ON DELETE CASCADE,
required_methods TEXT[] NOT NULL,
reason TEXT NOT NULL,
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
completed BOOLEAN DEFAULT FALSE
);

-- Parent approval requests
CREATE TABLE parent_approval_requests (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
child_identity_id UUID NOT NULL REFERENCES identities(id) ON DELETE CASCADE,
parent_identity_id UUID NOT NULL REFERENCES identities(id) ON DELETE CASCADE,
action TEXT NOT NULL,
resource_type TEXT NOT NULL,
resource_id UUID,
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'denied')),
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
decided_at TIMESTAMP WITH TIME ZONE
);

This is the foundation for truly family-friendly, accessible, secure AI. A platform where a grandmother, a dad, an 8-year-old, and an au-pair can all share the same Morphee — each authenticating in the way that works best for them.