The Context Forge Electron application now supports multiple authentication profiles, allowing users to easily switch between different backend environments and accounts without editing configuration files.
- Multiple Profiles: Create and manage unlimited authentication profiles
- Quick Switching: Switch between profiles with a single click
- Secure Storage: Credentials encrypted using
electron-store - Environment Tags: Label profiles by environment (Production, Staging, Development, Local)
- Auto-Migration: Automatically migrates existing
.envcredentials to profile system - Profile Metadata: Add descriptions and custom metadata to profiles
- Last Used Tracking: See when each profile was last accessed
- Encrypted Credentials: Passwords stored with AES encryption
- Separate Storage: Credentials stored separately from profile metadata
- Auto-generated Keys: Encryption keys automatically generated and secured
- Future Support: Architecture ready for OS keychain integration (Phase 2)
┌─────────────────────────────────────────────────────────────┐
│ Renderer Process │
├─────────────────────────────────────────────────────────────┤
│ UI Components │
│ ├── ProfileSelector (TopNav dropdown) │
│ ├── ProfileForm (Create/Edit) │
│ └── ProfileManagementPage (Full management interface) │
├─────────────────────────────────────────────────────────────┤
│ React Hooks │
│ ├── useProfiles() - Complete profile management │
│ ├── useCurrentProfile() - Current profile state │
│ └── useProfileAuth() - Authentication status │
├─────────────────────────────────────────────────────────────┤
│ IPC API Wrapper │
│ └── profile-api-ipc.ts - Type-safe IPC communication │
└─────────────────────────────────────────────────────────────┘
↕ IPC
┌─────────────────────────────────────────────────────────────┐
│ Main Process │
├─────────────────────────────────────────────────────────────┤
│ Profile Manager │
│ ├── Business logic │
│ ├── Validation │
│ ├── Event emission │
│ └── Auto-migration │
├─────────────────────────────────────────────────────────────┤
│ Profile Storage │
│ ├── Encrypted credential storage │
│ ├── Profile metadata storage │
│ └── CRUD operations │
└─────────────────────────────────────────────────────────────┘
interface AuthProfile {
id: string; // Unique identifier (hash of email + API URL)
name: string; // Display name
email: string; // Login email
apiUrl: string; // Backend API URL
isActive: boolean; // Currently active profile
createdAt: Date; // Creation timestamp
lastUsed?: Date; // Last usage timestamp
metadata?: {
description?: string; // User notes
environment?: string; // Environment tag
color?: string; // UI color (future)
icon?: string; // UI icon (future)
};
}Profile Metadata (context-forge-profiles.json):
- Non-sensitive profile information
- Settings and preferences
- Active profile ID
Encrypted Credentials (context-forge-credentials.json):
- Encrypted with AES-256
- Separate file for security
- Auto-generated encryption key
Encryption Key (context-forge-keys.json):
- Auto-generated on first use
- Used to encrypt/decrypt credentials
- Click the profile selector in the top navigation
- Select "Add Profile"
- Fill in the form:
- Profile Name: Descriptive name (e.g., "Production", "Local Dev")
- Email: Your login email
- Password: Your password
- API URL: Backend URL (e.g.,
http://localhost:4444) - Environment: Tag for organization
- Description: Optional notes
- Click "Create Profile"
Method 1: Quick Switch (TopNav)
- Click the profile selector
- Select the profile you want to switch to
- Authentication happens automatically
Method 2: Profile Management Page
- Navigate to Settings → Profiles
- Click "Switch" on the desired profile
- Click profile selector → "Manage Profiles"
- View all profiles with details
- Edit, delete, or switch profiles
- Active profile is highlighted
On first launch, the system automatically:
- Detects
.envcredentials - Creates "Default Profile (Migrated)"
- Sets it as active
- Suggests removing
.envcredentials
import { useProfiles } from '../hooks/useProfiles';
function MyComponent() {
const {
profiles, // All profiles
currentProfile, // Active profile
loading, // Loading state
error, // Error state
createProfile, // Create new profile
updateProfile, // Update existing profile
deleteProfile, // Delete profile
switchProfile, // Switch to different profile
loginWithProfile, // Login with specific profile
logout, // Logout current profile
refresh // Refresh profile list
} = useProfiles();
// Use the hooks...
}const result = await createProfile({
name: 'Production',
email: 'user@example.com',
password: 'secure-password',
apiUrl: 'https://api.production.com',
metadata: {
environment: 'production',
description: 'Production environment'
}
});
if (result.success) {
console.log('Profile created:', result.data);
} else {
console.error('Error:', result.error);
}import { profileManager } from '../services/ProfileManager';
profileManager.onProfileEvent((event) => {
switch (event.type) {
case 'profile-created':
console.log('New profile:', event.profile);
break;
case 'profile-switched':
console.log('Switched to:', event.profile);
break;
case 'profile-login-success':
console.log('Login successful:', event.token);
break;
}
});class ProfileManager {
// Initialize the manager
async initialize(): Promise<void>
// Profile CRUD
async createProfile(request: ProfileCreateRequest): Promise<ProfileStorageResult<AuthProfile>>
async getAllProfiles(): Promise<ProfileStorageResult<AuthProfile[]>>
async getProfile(profileId: string): Promise<ProfileStorageResult<AuthProfile>>
async updateProfile(profileId: string, updates: ProfileUpdateRequest): Promise<ProfileStorageResult<AuthProfile>>
async deleteProfile(profileId: string): Promise<ProfileStorageResult<void>>
// Authentication
async switchProfile(profileId: string): Promise<ProfileLoginResult>
async loginWithProfile(profileId: string): Promise<ProfileLoginResult>
async logout(): Promise<void>
// State
getCurrentProfile(): AuthProfile | null
getCurrentToken(): string | null
// Events
onProfileEvent(handler: ProfileEventHandler): void
offProfileEvent(handler: ProfileEventHandler): void
}class ProfileStorage {
// Profile operations
async createProfile(request: ProfileCreateRequest): Promise<ProfileStorageResult<AuthProfile>>
async getAllProfiles(): Promise<ProfileStorageResult<AuthProfile[]>>
async getProfile(profileId: string): Promise<ProfileStorageResult<AuthProfile>>
async getProfileCredentials(profileId: string): Promise<ProfileStorageResult<ProfileCredentials>>
async updateProfile(profileId: string, updates: ProfileUpdateRequest): Promise<ProfileStorageResult<AuthProfile>>
async deleteProfile(profileId: string): Promise<ProfileStorageResult<void>>
// Active profile management
async setActiveProfile(profileId: string): Promise<ProfileStorageResult<void>>
async getActiveProfile(): Promise<ProfileStorageResult<AuthProfile>>
// Settings
getSettings(): ProfileStore['settings']
updateSettings(settings: Partial<ProfileStore['settings']>): void
// Utility
async exportProfiles(): Promise<ProfileStorageResult<AuthProfile[]>>
async clearAll(): Promise<void>
}interface ProfileSettings {
storageMethod: 'encrypted-store' | 'keychain'; // Storage method
requireMasterPassword: boolean; // Master password (future)
autoLockTimeout: number; // Auto-lock timeout in minutes
rememberLastProfile: boolean; // Remember last used profile
}The system still supports .env for initial setup:
# Will be auto-migrated to profile on first launch
VITE_API_EMAIL=admin@example.com
VITE_API_PASSWORD=changeme
VITE_API_URL=http://localhost:4444- Use Strong Passwords: Each profile should have a strong, unique password
- Limit Production Access: Only create production profiles on trusted devices
- Regular Cleanup: Delete unused profiles
- Verify URLs: Always verify API URLs before saving
- Never Log Credentials: Ensure passwords are never logged
- Validate Input: Always validate profile data before storage
- Test Connections: Test credentials before saving profiles
- Handle Errors: Gracefully handle authentication failures
- Clear Memory: Clear sensitive data from memory after use
Symptom: Profile selector shows new profile but authentication fails
Solutions:
- Verify credentials are correct
- Check API URL is accessible
- Review browser console for errors
- Try logging out and back in
Symptom: Profile created but login fails
Solutions:
- Ensure password was entered correctly
- Check encryption key exists
- Verify storage permissions
- Try recreating the profile
Symptom: .env credentials not migrated
Solutions:
- Manually create profile with .env values
- Check .env file format
- Verify VITE_ prefix on variables
- Review console for migration errors
- OS Keychain Integration: Use macOS Keychain, Windows Credential Manager, Linux Secret Service
- Master Password: Optional master password for all profiles
- Biometric Auth: Touch ID / Windows Hello support
- Profile Sync: Sync profiles across devices (encrypted)
- Profile Templates: Pre-configured profile templates
- Profile Groups: Organize profiles into groups
- Quick Switch Shortcuts: Keyboard shortcuts for profile switching
- Session Management: Multiple concurrent sessions
- Audit Logging: Track profile access and changes
- Profile import/export with encryption
- Profile sharing (credentials excluded)
- Custom profile colors and icons
- Profile usage analytics
- Auto-lock on inactivity
- Two-factor authentication support
When contributing to the profile system:
- Maintain Security: Never compromise credential security
- Test Thoroughly: Test all profile operations
- Update Docs: Keep documentation current
- Follow Patterns: Use existing patterns and conventions
- Handle Errors: Provide clear error messages
For issues or questions:
- Check this documentation
- Review console logs
- Check GitHub issues
- Create new issue with details
Same as Context Forge Electron project license.