- Created comprehensive design document (docs/CHANNEL_LOCK_DESIGN.md) - Complete UX flows with PIN setup, unlock, and auto-lock modes - Technical implementation details with code examples - Security architecture (PBKDF2, brute force protection) - Database schema changes and migration plan - Testing strategy and implementation checklist - Added to ROADMAP.md as Phase 3, Item #1 (HIGH PRIORITY) - Target: October 2025 implementation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
734 lines
26 KiB
Markdown
734 lines
26 KiB
Markdown
# TeleBot Channel Lock & PIN Protection Design
|
||
|
||
**Status**: 📋 Documented - Ready for Implementation
|
||
**Priority**: 🔒 HIGH - Security & Privacy Enhancement
|
||
**Target**: Q4 2025 (October 2025)
|
||
**Estimated Implementation**: 2-3 hours core functionality + 1-2 hours UX polish
|
||
**Last Updated**: October 6, 2025
|
||
|
||
---
|
||
|
||
## Executive Summary
|
||
|
||
Complete channel lock system for TeleBot that provides enterprise-grade privacy protection through a 4-digit PIN. When enabled, the entire Telegram conversation is locked with a single security gate, requiring PIN entry to access any functionality beyond the unlock screen.
|
||
|
||
### Key Benefits
|
||
|
||
✅ **Single Security Boundary** - One gate protects all features, not selective locks
|
||
✅ **Screenshot Safe** - Locked screen shows no sensitive data
|
||
✅ **Shared Device Safe** - Complete protection for shared/public devices
|
||
✅ **Mobile Banking UX** - Familiar pattern from banking/crypto wallet apps
|
||
✅ **No Data Loss** - All data preserved in session, just visibility controlled
|
||
✅ **Privacy by Design** - User controls when and how data is visible
|
||
✅ **Simple Implementation** - Early gate check, minimal code complexity
|
||
|
||
---
|
||
|
||
## Architecture Overview
|
||
|
||
### Core Concept
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ ALL TELEGRAM UPDATES │
|
||
│ (Messages, Callbacks, Commands) │
|
||
└──────────────────┬──────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ SECURITY GATE │
|
||
│ Is session locked? │
|
||
└─────────┬───────────┘
|
||
│
|
||
┌─────────┴──────────┐
|
||
│ │
|
||
▼ ▼
|
||
🔒 LOCKED ✅ UNLOCKED
|
||
Show unlock Process normally
|
||
screen only (full features)
|
||
```
|
||
|
||
### State Model
|
||
|
||
```csharp
|
||
public enum SessionState
|
||
{
|
||
Guest, // No PIN set, full access (backward compatible with existing users)
|
||
Unlocked, // PIN set, currently unlocked (full access)
|
||
Locked // PIN set, currently locked (GATE BLOCKS ALL ACTIONS)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## User Experience Design
|
||
|
||
### 1. First-Time Setup (Optional)
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ 🔐 Secure Your Orders │
|
||
├─────────────────────────────────────┤
|
||
│ Protect your personal information │
|
||
│ and order history with a 4-digit │
|
||
│ PIN code. │
|
||
│ │
|
||
│ • Lock chat automatically │
|
||
│ • Protect payment details │
|
||
│ • Secure order history │
|
||
│ │
|
||
│ [✅ Set Up PIN] [⏭️ Skip] │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
**Design Decisions:**
|
||
- Completely optional - existing users continue with Guest mode
|
||
- Clear value proposition - explain what PIN protects
|
||
- No friction - skip option for users who don't need it
|
||
- Re-prompt after 3 orders or 7 days (gentle nudge)
|
||
|
||
### 2. PIN Setup Flow
|
||
|
||
```
|
||
Step 1: Enter New PIN
|
||
┌─────────────────────────────────────┐
|
||
│ 🔢 Create Your PIN │
|
||
├─────────────────────────────────────┤
|
||
│ PIN: ● ● ● ● │
|
||
│ │
|
||
│ [1] [2] [3] │
|
||
│ [4] [5] [6] │
|
||
│ [7] [8] [9] │
|
||
│ [⬅️ Clear] [0] [✅ Next] │
|
||
└─────────────────────────────────────┘
|
||
|
||
Step 2: Confirm PIN
|
||
┌─────────────────────────────────────┐
|
||
│ 🔢 Confirm Your PIN │
|
||
├─────────────────────────────────────┤
|
||
│ Enter your PIN again to confirm │
|
||
│ │
|
||
│ PIN: ● ● ● ● │
|
||
│ │
|
||
│ [Numeric keypad as above] │
|
||
└─────────────────────────────────────┘
|
||
|
||
Step 3: Success
|
||
┌─────────────────────────────────────┐
|
||
│ ✅ PIN Protection Enabled │
|
||
├─────────────────────────────────────┤
|
||
│ Your chat is now protected with │
|
||
│ a secure PIN. │
|
||
│ │
|
||
│ Auto-lock: 🏠 On Main Menu │
|
||
│ │
|
||
│ [⚙️ Settings] [🏠 Main Menu] │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
### 3. Locked State (Primary UX)
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ 🔒 Channel Locked │
|
||
├─────────────────────────────────────┤
|
||
│ Enter your 4-digit PIN to continue │
|
||
│ │
|
||
│ Locked at: 14:32 │
|
||
│ │
|
||
│ [🔓 Unlock] │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
**Important:** This is the ONLY message shown when locked. No other features accessible.
|
||
|
||
### 4. Unlock Flow
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ 🔢 Enter PIN │
|
||
├─────────────────────────────────────┤
|
||
│ PIN: ● ● ○ ○ │
|
||
│ │
|
||
│ Attempts remaining: 3 │
|
||
│ │
|
||
│ [1] [2] [3] │
|
||
│ [4] [5] [6] │
|
||
│ [7] [8] [9] │
|
||
│ [⬅️ Clear] [0] [✅ Submit] │
|
||
└─────────────────────────────────────┘
|
||
|
||
After Successful Unlock:
|
||
┌─────────────────────────────────────┐
|
||
│ ✅ Unlocked │
|
||
├─────────────────────────────────────┤
|
||
│ Welcome back! │
|
||
│ │
|
||
│ [Returns to normal menu] │
|
||
└─────────────────────────────────────┘
|
||
|
||
Failed Attempt:
|
||
┌─────────────────────────────────────┐
|
||
│ ❌ Incorrect PIN │
|
||
├─────────────────────────────────────┤
|
||
│ PIN: ○ ○ ○ ○ │
|
||
│ │
|
||
│ Attempts remaining: 2 │
|
||
│ ⚠️ Account locks after 3 failures │
|
||
│ │
|
||
│ [Numeric keypad] │
|
||
└─────────────────────────────────────┘
|
||
|
||
Account Locked (after 3 failures):
|
||
┌─────────────────────────────────────┐
|
||
│ 🚫 Account Temporarily Locked │
|
||
├─────────────────────────────────────┤
|
||
│ Too many failed attempts. │
|
||
│ │
|
||
│ Try again in: 5 minutes │
|
||
│ │
|
||
│ [❓ Forgot PIN?] │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
### 5. Auto-Lock Modes
|
||
|
||
```
|
||
⚙️ Security Settings
|
||
├── 🔐 PIN Protection: ✅ Enabled
|
||
│ ├── 📌 Change PIN
|
||
│ └── ❌ Disable PIN (requires current PIN)
|
||
│
|
||
├── 🔒 Auto-Lock: 🏠 On Main Menu ▼
|
||
│ │
|
||
│ ├── ✋ Manual Only
|
||
│ │ └── Lock only when "🔒 Lock" button clicked
|
||
│ │
|
||
│ ├── 🏠 On Main Menu (Recommended)
|
||
│ │ └── Auto-lock when returning to main menu
|
||
│ │
|
||
│ ├── ⏰ After Inactivity
|
||
│ │ └── Lock after [30 ▼] minutes idle
|
||
│ │
|
||
│ ├── 🚪 On Exit
|
||
│ │ └── Lock on any "back to menu" action
|
||
│ │
|
||
│ └── 🔐 Always (Paranoid Mode)
|
||
│ └── Lock after EVERY single action
|
||
│
|
||
├── 🗑️ Clear All Data (requires PIN)
|
||
│ └── Permanently delete order history, cart, settings
|
||
│
|
||
└── ℹ️ Security Info
|
||
├── PIN Last Changed: Oct 6, 2025
|
||
├── Last Locked: 2 hours ago
|
||
└── Failed Attempts Today: 0
|
||
```
|
||
|
||
---
|
||
|
||
## Technical Implementation
|
||
|
||
### 1. Database Schema Changes
|
||
|
||
```csharp
|
||
public class UserSession
|
||
{
|
||
// Existing fields...
|
||
public Guid Id { get; set; }
|
||
public long TelegramUserId { get; set; }
|
||
public Cart Cart { get; set; }
|
||
public List<ConversationMessage> Conversation { get; set; }
|
||
|
||
// NEW: Security & Lock State
|
||
public SessionState State { get; set; } = SessionState.Guest;
|
||
public string? PinHash { get; set; } // PBKDF2 hash, null = no PIN
|
||
public DateTime? LastActivityAt { get; set; }
|
||
public DateTime? LockedAt { get; set; }
|
||
public int FailedPinAttempts { get; set; }
|
||
public DateTime? LockoutUntil { get; set; }
|
||
public SecuritySettings Security { get; set; } = new();
|
||
public string? PinEntry { get; set; } // Temporary, in-memory only (not persisted)
|
||
}
|
||
|
||
public class SecuritySettings
|
||
{
|
||
public bool PinEnabled { get; set; }
|
||
public AutoLockMode AutoLock { get; set; } = AutoLockMode.OnMainMenu;
|
||
public int AutoLockMinutes { get; set; } = 30;
|
||
public DateTime? PinCreatedAt { get; set; }
|
||
public DateTime? PinLastChangedAt { get; set; }
|
||
}
|
||
|
||
public enum AutoLockMode
|
||
{
|
||
Manual, // Only lock when user clicks button
|
||
OnMainMenu, // Lock when returning to main menu (default)
|
||
OnInactivity, // Lock after X minutes idle
|
||
OnExit, // Lock on any "back to menu" action
|
||
Always // Lock after every single action (paranoid mode)
|
||
}
|
||
```
|
||
|
||
### 2. Security Gate Implementation
|
||
|
||
```csharp
|
||
// CallbackHandler.cs - Primary entry point for all user interactions
|
||
public async Task Handle(ITelegramBotClient bot, CallbackQuery callbackQuery, CancellationToken ct)
|
||
{
|
||
var telegramUser = callbackQuery.From;
|
||
var session = await _sessionManager.GetOrCreateSessionAsync(telegramUser.Id);
|
||
|
||
// UPDATE ACTIVITY TIMESTAMP
|
||
session.LastActivityAt = DateTime.UtcNow;
|
||
|
||
// SECURITY GATE - Check lock state BEFORE any other processing
|
||
if (session.State == SessionState.Locked && !IsUnlockAction(callbackQuery.Data))
|
||
{
|
||
await ShowUnlockScreen(bot, callbackQuery);
|
||
return; // STOP ALL PROCESSING - gate closed
|
||
}
|
||
|
||
// Check if account is in lockout period
|
||
if (session.LockoutUntil.HasValue && DateTime.UtcNow < session.LockoutUntil.Value)
|
||
{
|
||
await ShowLockoutScreen(bot, callbackQuery, session.LockoutUntil.Value);
|
||
return;
|
||
}
|
||
|
||
// If unlocked or this is unlock action, continue normal flow
|
||
await ProcessCallback(bot, callbackQuery, session);
|
||
|
||
// CHECK AUTO-LOCK CONDITIONS after action completes
|
||
await CheckAndApplyAutoLock(session);
|
||
}
|
||
|
||
private bool IsUnlockAction(string? callbackData)
|
||
{
|
||
return callbackData?.StartsWith("unlock") == true ||
|
||
callbackData?.StartsWith("pin_entry:") == true ||
|
||
callbackData == "show_pin_pad" ||
|
||
callbackData == "pin_clear" ||
|
||
callbackData?.StartsWith("pin_digit:") == true;
|
||
}
|
||
```
|
||
|
||
### 3. PIN Management
|
||
|
||
```csharp
|
||
// PIN Hashing (secure storage)
|
||
public string HashPin(string pin)
|
||
{
|
||
using var rfc2898 = new Rfc2898DeriveBytes(
|
||
pin,
|
||
saltSize: 32,
|
||
iterations: 100_000,
|
||
HashAlgorithmName.SHA256
|
||
);
|
||
|
||
var hash = Convert.ToBase64String(rfc2898.GetBytes(32));
|
||
var salt = Convert.ToBase64String(rfc2898.Salt);
|
||
|
||
return $"{salt}:{hash}"; // Store both salt and hash
|
||
}
|
||
|
||
// PIN Verification
|
||
public bool VerifyPin(string enteredPin, string storedHash)
|
||
{
|
||
var parts = storedHash.Split(':');
|
||
if (parts.Length != 2) return false;
|
||
|
||
var salt = Convert.FromBase64String(parts[0]);
|
||
var hash = Convert.FromBase64String(parts[1]);
|
||
|
||
using var rfc2898 = new Rfc2898DeriveBytes(
|
||
enteredPin,
|
||
salt,
|
||
iterations: 100_000,
|
||
HashAlgorithmName.SHA256
|
||
);
|
||
|
||
var enteredHash = rfc2898.GetBytes(32);
|
||
return CryptographicOperations.FixedTimeEquals(hash, enteredHash);
|
||
}
|
||
|
||
// PIN Entry Handling
|
||
private async Task HandlePinDigit(ITelegramBotClient bot, CallbackQuery query, UserSession session, string digit)
|
||
{
|
||
if (session.PinEntry == null)
|
||
session.PinEntry = "";
|
||
|
||
if (digit == "clear")
|
||
{
|
||
session.PinEntry = "";
|
||
}
|
||
else if (digit == "submit" && session.PinEntry.Length == 4)
|
||
{
|
||
await HandlePinSubmit(bot, query, session);
|
||
return;
|
||
}
|
||
else if (session.PinEntry.Length < 4)
|
||
{
|
||
session.PinEntry += digit;
|
||
}
|
||
|
||
// Refresh PIN pad with updated dots
|
||
await ShowPinPad(bot, query, session);
|
||
}
|
||
|
||
private async Task HandlePinSubmit(ITelegramBotClient bot, CallbackQuery query, UserSession session)
|
||
{
|
||
if (VerifyPin(session.PinEntry, session.PinHash!))
|
||
{
|
||
// SUCCESS
|
||
session.State = SessionState.Unlocked;
|
||
session.FailedPinAttempts = 0;
|
||
session.PinEntry = null;
|
||
|
||
await bot.EditMessageTextAsync(
|
||
query.Message!.Chat.Id,
|
||
query.Message.MessageId,
|
||
"✅ *Unlocked*\n\nWelcome back!",
|
||
parseMode: ParseMode.Markdown,
|
||
replyMarkup: MenuBuilder.MainMenu()
|
||
);
|
||
}
|
||
else
|
||
{
|
||
// FAILURE
|
||
session.FailedPinAttempts++;
|
||
session.PinEntry = null;
|
||
|
||
if (session.FailedPinAttempts >= 3)
|
||
{
|
||
// LOCKOUT
|
||
session.LockoutUntil = DateTime.UtcNow.AddMinutes(5);
|
||
await ShowLockoutScreen(bot, query, session.LockoutUntil.Value);
|
||
}
|
||
else
|
||
{
|
||
await ShowPinPad(bot, query, session, isError: true);
|
||
}
|
||
}
|
||
|
||
await _sessionManager.SaveSessionAsync(session);
|
||
}
|
||
```
|
||
|
||
### 4. Auto-Lock Logic
|
||
|
||
```csharp
|
||
private async Task CheckAndApplyAutoLock(UserSession session)
|
||
{
|
||
// Don't lock if PIN not enabled or already locked
|
||
if (!session.Security.PinEnabled || session.State == SessionState.Locked)
|
||
return;
|
||
|
||
bool shouldLock = session.Security.AutoLock switch
|
||
{
|
||
AutoLockMode.Manual => false, // Never auto-lock
|
||
|
||
AutoLockMode.OnMainMenu =>
|
||
session.CurrentMenu == "main" || session.LastAction == "menu",
|
||
|
||
AutoLockMode.OnInactivity =>
|
||
(DateTime.UtcNow - session.LastActivityAt.GetValueOrDefault()).TotalMinutes
|
||
>= session.Security.AutoLockMinutes,
|
||
|
||
AutoLockMode.OnExit =>
|
||
session.CurrentMenu == "main" ||
|
||
session.LastAction == "back" ||
|
||
session.LastAction == "menu",
|
||
|
||
AutoLockMode.Always => true, // Lock after every action
|
||
|
||
_ => false
|
||
};
|
||
|
||
if (shouldLock)
|
||
{
|
||
session.State = SessionState.Locked;
|
||
session.LockedAt = DateTime.UtcNow;
|
||
session.PinEntry = null; // Clear any temporary PIN entry
|
||
await _sessionManager.SaveSessionAsync(session);
|
||
|
||
_logger.LogInformation(
|
||
"Session {SessionId} auto-locked (mode: {Mode})",
|
||
session.Id,
|
||
session.Security.AutoLock
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5. UI Components
|
||
|
||
```csharp
|
||
// MenuBuilder.cs additions
|
||
|
||
public InlineKeyboardMarkup UnlockScreen()
|
||
{
|
||
return new InlineKeyboardMarkup(new[]
|
||
{
|
||
new[]
|
||
{
|
||
InlineKeyboardButton.WithCallbackData("🔓 Unlock", "show_pin_pad")
|
||
}
|
||
});
|
||
}
|
||
|
||
public InlineKeyboardMarkup PinPad(UserSession session, bool isError = false)
|
||
{
|
||
var maskLength = session.PinEntry?.Length ?? 0;
|
||
var mask = new string('●', maskLength) + new string('○', 4 - maskLength);
|
||
|
||
return new InlineKeyboardMarkup(new[]
|
||
{
|
||
new[]
|
||
{
|
||
InlineKeyboardButton.WithCallbackData("1", "pin_digit:1"),
|
||
InlineKeyboardButton.WithCallbackData("2", "pin_digit:2"),
|
||
InlineKeyboardButton.WithCallbackData("3", "pin_digit:3")
|
||
},
|
||
new[]
|
||
{
|
||
InlineKeyboardButton.WithCallbackData("4", "pin_digit:4"),
|
||
InlineKeyboardButton.WithCallbackData("5", "pin_digit:5"),
|
||
InlineKeyboardButton.WithCallbackData("6", "pin_digit:6")
|
||
},
|
||
new[]
|
||
{
|
||
InlineKeyboardButton.WithCallbackData("7", "pin_digit:7"),
|
||
InlineKeyboardButton.WithCallbackData("8", "pin_digit:8"),
|
||
InlineKeyboardButton.WithCallbackData("9", "pin_digit:9")
|
||
},
|
||
new[]
|
||
{
|
||
InlineKeyboardButton.WithCallbackData("⬅️ Clear", "pin_clear"),
|
||
InlineKeyboardButton.WithCallbackData("0", "pin_digit:0"),
|
||
InlineKeyboardButton.WithCallbackData("✅ Submit", "pin_submit")
|
||
}
|
||
});
|
||
}
|
||
|
||
// Add lock button to main menu when PIN enabled
|
||
public InlineKeyboardMarkup MainMenu(UserSession? session = null)
|
||
{
|
||
var buttons = new List<InlineKeyboardButton[]>
|
||
{
|
||
// ... existing menu items
|
||
};
|
||
|
||
// Add lock button if PIN is enabled
|
||
if (session?.Security.PinEnabled == true && session.State == SessionState.Unlocked)
|
||
{
|
||
buttons.Add(new[]
|
||
{
|
||
InlineKeyboardButton.WithCallbackData("🔒 Lock Chat", "lock_channel")
|
||
});
|
||
}
|
||
|
||
return new InlineKeyboardMarkup(buttons);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Security Considerations
|
||
|
||
### 1. PIN Storage
|
||
- ✅ PBKDF2 with 100,000 iterations (OWASP recommended)
|
||
- ✅ 32-byte random salt per PIN
|
||
- ✅ SHA-256 hash algorithm
|
||
- ✅ Constant-time comparison (prevents timing attacks)
|
||
- ❌ **Never** store PIN in plaintext
|
||
|
||
### 2. Brute Force Protection
|
||
- ✅ 3 attempts before 5-minute lockout
|
||
- ✅ Progressive lockout (future: 5min → 15min → 1hr → 24hr)
|
||
- ✅ Failed attempt logging for audit trail
|
||
- ✅ Rate limiting on unlock endpoint
|
||
|
||
### 3. Session Security
|
||
- ✅ PinEntry stored in memory only, never persisted to database
|
||
- ✅ Auto-clear PinEntry on lock/unlock/failure
|
||
- ✅ Session state synchronized across all interactions
|
||
- ✅ Activity timestamp updated on every interaction
|
||
|
||
### 4. Data Protection
|
||
- ✅ All sensitive data (orders, payments, addresses) hidden when locked
|
||
- ✅ No data exposed in locked state messages
|
||
- ✅ Session data preserved but inaccessible
|
||
- ✅ Optional "Clear All Data" requires PIN verification
|
||
|
||
---
|
||
|
||
## Migration & Backward Compatibility
|
||
|
||
### Existing Users (Guest Mode)
|
||
- No disruption - continue using bot without PIN
|
||
- Optional prompt after 3 orders or 7 days: "Would you like to secure your orders with a PIN?"
|
||
- Can enable PIN anytime from Settings menu
|
||
|
||
### Database Migration
|
||
```sql
|
||
-- Add new columns to UserSessions table
|
||
ALTER TABLE UserSessions ADD COLUMN State INTEGER DEFAULT 0; -- 0 = Guest
|
||
ALTER TABLE UserSessions ADD COLUMN PinHash TEXT NULL;
|
||
ALTER TABLE UserSessions ADD COLUMN LastActivityAt TEXT NULL;
|
||
ALTER TABLE UserSessions ADD COLUMN LockedAt TEXT NULL;
|
||
ALTER TABLE UserSessions ADD COLUMN FailedPinAttempts INTEGER DEFAULT 0;
|
||
ALTER TABLE UserSessions ADD COLUMN LockoutUntil TEXT NULL;
|
||
ALTER TABLE UserSessions ADD COLUMN SecuritySettings TEXT NULL; -- JSON
|
||
|
||
-- Add indexes for performance
|
||
CREATE INDEX IX_UserSessions_State ON UserSessions(State);
|
||
CREATE INDEX IX_UserSessions_LockoutUntil ON UserSessions(LockoutUntil);
|
||
```
|
||
|
||
---
|
||
|
||
## Testing Strategy
|
||
|
||
### Unit Tests
|
||
- ✅ PIN hashing and verification
|
||
- ✅ Auto-lock mode logic
|
||
- ✅ Lockout calculation
|
||
- ✅ PIN entry validation (4 digits only)
|
||
|
||
### Integration Tests
|
||
- ✅ Full unlock flow (locked → pin entry → unlocked)
|
||
- ✅ Failed attempt lockout enforcement
|
||
- ✅ Auto-lock triggers (menu, inactivity, exit)
|
||
- ✅ State transitions (Guest → Unlocked → Locked)
|
||
|
||
### E2E Tests (Playwright)
|
||
- ✅ First-time PIN setup flow
|
||
- ✅ Lock and unlock via menu
|
||
- ✅ Auto-lock on main menu return
|
||
- ✅ Failed attempts and lockout
|
||
- ✅ PIN change workflow
|
||
- ✅ Disable PIN and return to Guest mode
|
||
|
||
---
|
||
|
||
## Implementation Checklist
|
||
|
||
### Phase 1: Core Functionality (2-3 hours)
|
||
- [ ] Add `SessionState`, `PinHash`, security fields to `UserSession` model
|
||
- [ ] Create database migration for new columns
|
||
- [ ] Implement `HashPin()` and `VerifyPin()` methods
|
||
- [ ] Add security gate at top of `CallbackHandler.Handle()`
|
||
- [ ] Create `ShowUnlockScreen()` method
|
||
- [ ] Build PIN pad UI in `MenuBuilder.PinPad()`
|
||
- [ ] Implement `HandlePinDigit()` and `HandlePinSubmit()`
|
||
- [ ] Add lockout logic (3 attempts → 5 min timeout)
|
||
- [ ] Implement manual lock button in main menu
|
||
|
||
### Phase 2: Auto-Lock (1 hour)
|
||
- [ ] Create `CheckAndApplyAutoLock()` method
|
||
- [ ] Implement all 5 auto-lock modes
|
||
- [ ] Add activity timestamp tracking
|
||
- [ ] Test auto-lock triggers
|
||
|
||
### Phase 3: Settings & Management (1 hour)
|
||
- [ ] Create Security Settings menu
|
||
- [ ] Implement PIN setup flow (new users)
|
||
- [ ] Implement Change PIN flow
|
||
- [ ] Implement Disable PIN flow (requires current PIN)
|
||
- [ ] Add auto-lock mode selector
|
||
- [ ] Add "Clear All Data" with PIN verification
|
||
|
||
### Phase 4: UX Polish (1-2 hours)
|
||
- [ ] Add first-time setup prompt (optional, after 3 orders)
|
||
- [ ] Improve error messages and feedback
|
||
- [ ] Add security info display (last locked, failed attempts)
|
||
- [ ] Add countdown timer for lockout screen
|
||
- [ ] Implement "Forgot PIN?" recovery flow (contact support)
|
||
- [ ] Add haptic/visual feedback for PIN entry
|
||
|
||
### Phase 5: Testing & Deployment (1 hour)
|
||
- [ ] Write unit tests for PIN security
|
||
- [ ] Write integration tests for lock flows
|
||
- [ ] Manual E2E testing on Telegram
|
||
- [ ] Security review (penetration testing)
|
||
- [ ] Deploy to staging environment
|
||
- [ ] User acceptance testing
|
||
- [ ] Deploy to production with feature flag
|
||
|
||
---
|
||
|
||
## Future Enhancements
|
||
|
||
### Short Term (Q1 2026)
|
||
- **Biometric Unlock**: Support Telegram's WebApp biometric API
|
||
- **PIN Complexity**: Option for 6-digit PIN or alphanumeric password
|
||
- **Session Management**: "Lock all devices" from any session
|
||
- **Audit Log**: View all unlock events and failed attempts
|
||
|
||
### Medium Term (Q2 2026)
|
||
- **2FA Support**: Optional TOTP/authenticator app second factor
|
||
- **Trusted Devices**: Remember devices, require PIN only on new devices
|
||
- **Emergency Contacts**: Designate trusted contact for PIN recovery
|
||
- **Secure Backup**: Encrypted backup of order history with recovery phrase
|
||
|
||
### Long Term (Q3-Q4 2026)
|
||
- **Hardware Security**: Support for hardware security keys (YubiKey, etc.)
|
||
- **Zero-Knowledge Encryption**: End-to-end encrypted order data
|
||
- **Multi-Account**: Separate PINs for business vs personal shopping
|
||
- **Compliance**: FIPS 140-2 certification for payment card industry
|
||
|
||
---
|
||
|
||
## Success Metrics
|
||
|
||
### Security Metrics
|
||
- ✅ Zero unauthorized access incidents
|
||
- ✅ < 0.1% lockout rate (balance security vs UX)
|
||
- ✅ 100% PIN hash security (PBKDF2 with strong parameters)
|
||
|
||
### User Adoption
|
||
- 🎯 Target: 60% of active users enable PIN within 30 days
|
||
- 🎯 Target: < 5% disable PIN after enabling
|
||
- 🎯 Target: > 90% user satisfaction with lock/unlock UX
|
||
|
||
### Performance
|
||
- ✅ < 100ms PIN verification time
|
||
- ✅ < 500ms lock/unlock state transition
|
||
- ✅ Zero impact on unlocked session performance
|
||
|
||
---
|
||
|
||
## Open Questions
|
||
|
||
1. **Forgot PIN Flow**: Contact support vs security questions vs recovery phrase?
|
||
- **Recommendation**: Contact support with identity verification (Telegram username + recent order ID)
|
||
|
||
2. **PIN Complexity**: Force 4 digits or allow stronger PINs?
|
||
- **Recommendation**: Default 4 digits, optional 6-digit or alphanumeric in settings
|
||
|
||
3. **Session Expiry**: Should locked sessions expire and clear data after X days?
|
||
- **Recommendation**: 90-day expiry with warning email/notification at 80 days
|
||
|
||
4. **Multi-Device**: If user has bot open on phone and desktop, lock both?
|
||
- **Recommendation**: Yes, state is server-side, lock applies to all clients
|
||
|
||
5. **PIN Recovery**: Allow self-service PIN reset via email?
|
||
- **Recommendation**: Phase 1 = support only, Phase 2 = email with order verification
|
||
|
||
---
|
||
|
||
## References
|
||
|
||
- [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)
|
||
- [Telegram Bot API Security Best Practices](https://core.telegram.org/bots/security)
|
||
- [NIST Digital Identity Guidelines](https://pages.nist.gov/800-63-3/)
|
||
- [PBKDF2 Implementation Guide](https://cryptobook.nakov.com/mac-and-key-derivation/pbkdf2)
|
||
|
||
---
|
||
|
||
**Document Version**: 1.0
|
||
**Author**: SilverLabs Development Team
|
||
**Review Date**: October 6, 2025
|
||
**Next Review**: November 6, 2025 (post-implementation)
|