Files
Kecalek/specs/agent-d-auth-screens.md
filip fe861cfafa Initial commit: Kecalek Android client
Complete Android client for encrypted chat platform.
78+ Kotlin files: crypto (X3DH, Double Ratchet, AES-GCM, Ed25519, X25519,
RSA-PSS), network (TCP/TLS, 50 endpoints), Hilt DI, Room+SQLCipher DB,
Jetpack Compose UI with Catppuccin Mocha theme.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 01:19:17 +01:00

161 lines
5.6 KiB
Markdown

# Agent D: Auth Screens
## Phase: 2 (UI Shells)
## Depends on: Agent A (project), Agent B (theme + navigation)
## Context
You are building authentication screens for "Kecalek" encrypted chat app.
The login uses RSA challenge-response (no password sent to server).
Registration requires email verification code.
Device pairing allows linking a new device to an existing account.
## Task
Create Login, Register, Pairing screens and AuthViewModel skeleton.
## Files to Create
### 1. ui/auth/LoginScreen.kt
Jetpack Compose screen with:
- **App title** "Kecalek" at the top (headlineLarge)
- **Subtitle** "Encrypted Messaging" (bodyMedium, Subtext1 color)
- **Email/Username** text field (OutlinedTextField, Surface1 background)
- **Password** text field (password visibility toggle icon)
- **Login button** (filled, Lavender primary, full width)
- **"Create Account"** text button below (navigates to Register)
- **"Link Device"** text button below (navigates to Pairing)
- **Server config section** (expandable/collapsible):
- Host text field (default: "chat.ai-tech.news")
- Port text field (default: "9999")
- TLS toggle switch (default: off)
- **Loading state**: CircularProgressIndicator replacing login button
- **Error state**: Red error text below password field
- **Biometric login button** (fingerprint icon, shown only if biometric available)
**Layout**: Centered vertically, max width 400dp, padding 24dp.
### 2. ui/auth/RegisterScreen.kt
Jetpack Compose screen with:
- **Back arrow** in top bar (navigates back)
- **Title** "Create Account"
- **Username** text field
- **Email** text field
- **Password** text field (with visibility toggle)
- **Confirm Password** text field
- **Register button** (filled, Lavender, full width)
- **Confirmation code section** (shown after successful registration):
- Info text "Check your email for a verification code"
- 6-digit code input field
- Confirm button
- **Loading state** + **Error state** (same pattern as Login)
### 3. ui/auth/PairingScreen.kt
Jetpack Compose screen with:
- **Back arrow** in top bar
- **Title** "Link New Device"
- **Info text** explaining pairing process
- **8-digit pairing code** displayed prominently (headlineLarge, monospace, letter-spacing)
- **Countdown timer** or progress indicator showing poll status
- **"Waiting for authorization..."** text with animated dots
- **Cancel button** (outlined)
- **Status messages**: "Device authorized", "Pairing failed", etc.
### 4. ui/auth/AuthViewModel.kt
```kotlin
package com.kecalek.chat.ui.auth
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject
data class AuthUiState(
val isLoading: Boolean = false,
val error: String? = null,
val isLoggedIn: Boolean = false,
val isRegistered: Boolean = false,
val needsConfirmation: Boolean = false,
val pairingCode: String? = null,
val isPairingWaiting: Boolean = false,
val serverHost: String = "chat.ai-tech.news",
val serverPort: Int = 9999,
val useTls: Boolean = false,
val biometricAvailable: Boolean = false,
)
@HiltViewModel
class AuthViewModel @Inject constructor(
// TODO: Inject ChatClient, SessionManager
) : ViewModel() {
private val _uiState = MutableStateFlow(AuthUiState())
val uiState: StateFlow<AuthUiState> = _uiState.asStateFlow()
fun login(emailOrUsername: String, password: String) {
// TODO: Implement RSA challenge-response login
// 1. ChatClient.login(email, password)
// 2. On success: navigate to ConversationList
// 3. On failure: show error
}
fun register(username: String, email: String, password: String) {
// TODO: Implement registration
// 1. ChatClient.register(username, email, password)
// 2. On success: show confirmation code input
// 3. On failure: show error
}
fun confirmRegistration(email: String, code: String) {
// TODO: Confirm with 6-digit code
// 1. ChatClient.confirm_registration(email, code)
// 2. On success: auto-login
}
fun startPairing() {
// TODO: Start device pairing
// 1. ChatClient.pairing_start() -> get 8-digit code
// 2. Show code to user
// 3. Start polling for authorization
}
fun loginWithBiometric() {
// TODO: Biometric authentication
}
fun updateServerConfig(host: String, port: Int, useTls: Boolean) {
_uiState.value = _uiState.value.copy(
serverHost = host,
serverPort = port,
useTls = useTls,
)
}
fun clearError() {
_uiState.value = _uiState.value.copy(error = null)
}
}
```
## Reference: iOS LoginView behavior
- Login/Register are modes of the same view (iOS uses toggle tabs)
- Server configuration is expandable (collapsed by default)
- Biometric login uses Face ID/Touch ID
- Error messages appear below the form
- Loading spinner replaces the action button
## Constraints
- Use Material 3 components (OutlinedTextField, FilledTonalButton, etc.)
- Use `CatppuccinMocha` colors from theme
- Use `hiltViewModel()` for ViewModel injection
- All screens receive `navController: NavHostController` parameter
- Password fields must have visibility toggle
- Support keyboard "Done" action to submit form
- Use `rememberSaveable` for form state to survive config changes
## DO NOT
- Implement actual login/register/pairing logic (just skeleton functions)
- Handle cryptographic operations
- Store credentials or keys
- Modify navigation graph (screen placeholders already exist)