Files
self/docs/authentication-flows.md
Timur Gordon f970f3fb58 Add SelfFreezoneClient wrapper for Self components
- Created SelfFreezoneClient in Self components
- Wraps SDK FreezoneScriptClient for Self-specific operations
- Implements send_verification_email method
- Uses Rhai script template for email verification
- Includes template variable substitution
- Added serde-wasm-bindgen dependency

Usage:
  let client = SelfFreezoneClient::builder()
      .supervisor_url("http://localhost:8080")
      .secret("my-secret")
      .build()?;

  client.send_verification_email(
      "user@example.com",
      "123456",
      "https://verify.com/abc"
  ).await?;
2025-11-03 16:16:18 +01:00

8.6 KiB

Authentication Flows

Overview

Self implements a cryptographic challenge-response authentication system that eliminates the need for passwords while providing strong security guarantees. The system supports both new user registration and existing user authentication flows.

Registration Flow

Step-by-Step Process

sequenceDiagram
    participant U as User
    participant C as Client (Browser)
    participant S as Server
    participant E as Email System

    Note over U,E: Phase 1: Email Verification
    U->>C: Enter name and email
    C->>S: POST /api/send-verification
    S->>E: Generate verification link
    S->>C: Verification sent response
    S-->>U: Display verification link (dev mode)
    
    Note over U,E: Phase 2: Email Confirmation
    U->>S: Click verification link
    S->>S: Mark email as verified
    S->>C: SSE notification (verified)
    C->>C: Update UI to show verified status
    
    Note over U,E: Phase 3: Key Generation
    U->>C: Click "Generate Keys"
    C->>C: Generate secp256k1 key pair
    C->>C: Display private key for backup
    U->>C: Copy private key (mandatory)
    U->>C: Enter encryption password
    C->>C: Encrypt private key with AES-256-GCM
    C->>C: Store encrypted key in localStorage
    
    Note over U,E: Phase 4: Key Confirmation
    U->>C: Paste private key for confirmation
    C->>C: Verify pasted key matches generated key
    U->>C: Confirm registration
    C->>S: POST /api/register {email, name, public_key}
    S->>S: Verify email is confirmed
    S->>S: Store user record
    S->>C: Registration success response
    C->>C: Complete registration flow

Registration Data Flow

  1. Email Collection

    struct EmailVerificationRequest {
        email: String,
    }
    
  2. Verification Status

    struct VerificationStatus {
        email: String,
        verified: bool,
        verification_token: String,
    }
    
  3. Key Generation (Client-side)

    let keypair = generate_keypair()?;
    let encrypted_key = encrypt_private_key(&keypair.private_key, &password)?;
    
  4. Registration Completion

    struct RegistrationRequest {
        email: String,
        name: String,
        public_key: String,
    }
    

Security Measures

  • Email Verification: Prevents unauthorized registrations
  • Key Backup Confirmation: Ensures user has saved private key
  • Client-side Encryption: Private key never transmitted unencrypted
  • Secure Random Generation: Cryptographically secure key generation
  • Password Validation: Minimum 8 character password requirement

Login Flow

Step-by-Step Process

sequenceDiagram
    participant U as User
    participant C as Client (Browser)
    participant S as Server
    participant V as Vault System

    Note over U,V: Phase 1: Identity Input
    U->>C: Enter public key or select identity
    C->>C: Validate public key format
    
    Note over U,V: Phase 2: Challenge Generation
    C->>S: Request authentication challenge
    S->>S: Generate random challenge
    S->>C: Return challenge + session info
    
    Note over U,V: Phase 3: Key Decryption
    U->>C: Enter password
    C->>V: Retrieve encrypted private key
    V->>C: Return encrypted key data
    C->>C: Decrypt private key with password
    
    Note over U,V: Phase 4: Challenge Response
    C->>C: Sign challenge with private key
    C->>S: POST /oauth/token {signature, public_key, challenge}
    S->>S: Verify signature against public key
    S->>S: Generate JWT token
    S->>C: Return access token
    
    Note over U,V: Phase 5: Session Establishment
    C->>C: Store JWT token
    C->>S: GET /oauth/userinfo (with Bearer token)
    S->>C: Return user profile
    C->>C: Complete login flow

OAuth 2.0 Token Request

The login process follows OAuth 2.0 client credentials flow with cryptographic assertions:

struct LoginRequest {
    grant_type: String,              // "client_credentials"
    client_assertion_type: String,   // "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
    client_assertion: String,        // Signed JWT containing challenge response
    public_key: String,              // User's public key (client identifier)
    challenge: String,               // Server-provided challenge
    scope: String,                   // Requested permissions
}

JWT Token Structure

struct Claims {
    sub: String,        // Subject (public key)
    iss: String,        // Issuer ("self-sovereign-identity")
    aud: String,        // Audience ("identity-server")
    exp: usize,         // Expiration time (1 hour)
    iat: usize,         // Issued at time
    scope: String,      // Granted scopes
}

Challenge-Response Mechanism

  1. Challenge Generation

    let challenge = Uuid::new_v4().to_string();
    
  2. Signature Creation (Client-side)

    let signature = keypair.sign(&challenge)?;
    
  3. Signature Verification (Server-side)

    fn verify_signature(public_key: &str, challenge: &str, signature: &str) -> bool {
        // Verify signature matches challenge using public key
    }
    

Vault-Based Authentication

Multi-Key Management

The vault system allows users to store multiple encrypted keys and authenticate with any of them:

sequenceDiagram
    participant U as User
    participant C as Client
    participant V as Vault Manager
    participant S as Server

    U->>C: Select identity from vault
    C->>V: List available identities
    V->>C: Return identity list
    U->>C: Choose identity and enter password
    C->>V: Decrypt selected identity key
    V->>C: Return decrypted private key
    C->>S: Authenticate with selected identity
    S->>C: Return session for selected identity

Vault Data Structure

struct VaultEntry {
    id: String,
    name: String,
    email: String,
    public_key: String,
    encrypted_private_key: EncryptedPrivateKey,
    created_at: String,
}

struct EncryptedPrivateKey {
    encrypted_data: String,  // Base64 encoded ciphertext
    nonce: String,          // Base64 encoded nonce
    salt: String,           // Base64 encoded salt
}

Session Management

JWT Token Lifecycle

  1. Token Issuance

    • Generated after successful authentication
    • Contains user's public key as subject
    • 1-hour expiration time
    • Signed with server secret
  2. Token Usage

    • Included in Authorization header as Bearer token
    • Required for accessing protected endpoints
    • Validated on each request
  3. Token Refresh

    • Currently requires re-authentication
    • Future: Refresh token mechanism

Session Storage

// Client-side session storage
localStorage.setItem('jwt_token', access_token);
localStorage.setItem('current_identity', public_key);
localStorage.setItem('session_expires', expiration_time);

Error Handling

Registration Errors

  • Email Not Verified: User must complete email verification
  • Invalid Email Format: Client-side validation prevents submission
  • Key Generation Failed: Retry with new random seed
  • Encryption Failed: Check password strength and retry
  • Server Unavailable: Retry with exponential backoff

Authentication Errors

  • Invalid Public Key: Key format validation and user feedback
  • Wrong Password: Decryption failure, prompt for correct password
  • Signature Verification Failed: Invalid key or challenge tampering
  • Token Expired: Automatic re-authentication flow
  • User Not Found: Public key not registered, redirect to registration

Error Response Format

struct ErrorResponse {
    error: String,              // OAuth 2.0 error code
    error_description: String,  // Human-readable description
    error_uri: Option<String>,  // Optional documentation link
}

Security Considerations

Attack Mitigation

  1. Replay Attacks: Challenges are single-use and time-limited
  2. Man-in-the-Middle: HTTPS encryption for all communications
  3. Key Theft: Private keys encrypted with user passwords
  4. Brute Force: Rate limiting on authentication attempts
  5. Session Hijacking: JWT tokens with short expiration times

Privacy Protection

  • No Password Storage: Server never sees user passwords
  • Minimal Data Collection: Only email and public key stored
  • Local Key Storage: Private keys never leave user's device
  • Anonymous Usage: Public keys don't reveal personal information

Compliance Considerations

  • GDPR: Users control their own data, right to deletion
  • OAuth 2.0: Standard token-based authentication
  • OpenID Connect: Compatible user info endpoint
  • WebAuthn: Future integration for hardware token support