NAME session - RSX session management and authentication system SYNOPSIS Static session interface with lazy initialization and CLI mode support DESCRIPTION The RSX Session class provides a unified interface for session management and authentication. Unlike Laravel's session() helper which returns a session store, RSX uses static methods for direct access to session data. Sessions are always persistent (365 days) - there is no "remember me" option. The framework handles session creation, authentication, and security automatically. Key differences from Laravel: - Laravel: session()->put('user_id', $id) or Auth::login($user) - RSX: Session::set_user($user) or Session::set_user_id($user_id) Benefits: - Static interface accessible from anywhere - Automatic session creation only when needed - CSRF token management built-in - Secure cookie settings by default - CLI mode support (no database operations) - No session overhead until actually used LAZY INITIALIZATION Critical Concept: Sessions are NOT created until a method forces them to exist. Simply calling Session::get_user_id() or Session::get_site_id() will NOT create a session if none exists - they return null/0. Session is created when: - Session::set_user() or Session::set_user_id() is called - Session::set_site() or Session::set_site_id() is called - Session::get_session_id() is called - Session::get_session() is called Session is NOT created when: - Session::get_user_id() - returns null if no session - Session::get_site_id() - returns 0 if no session - Session::get_user() - returns null if no session - Session::get_site() - returns null if no session - Session::is_logged_in() - returns false if no session - Session::has_session() - returns false if no session - Session::init() - only loads existing session, never creates Example Flow: // Page load - no session created yet $user_id = Session::get_user_id(); // null, no session created // User logs in - NOW session is created Session::set_user_id(123); // Creates session with user_id=123 // Subsequent requests load the existing session $user_id = Session::get_user_id(); // 123, loads from cookie This lazy initialization prevents unnecessary database writes for unauthenticated visitors who never log in or interact with site-specific features. CLI MODE Behavior in CLI Context: When running in CLI mode (artisan commands, tests), Session operates in a degraded "mock" mode using static properties only: - No database reads or writes - No cookie operations - No HTTP headers sent - Session data stored in static properties only - get_client_ip() returns "CLI" Usage in CLI: // Set user and site for command context Session::set_user_id(123); Session::set_site_id(456); // All getter methods work normally $user_id = Session::get_user_id(); // 123 $site_id = Session::get_site_id(); // 456 $logged_in = Session::is_logged_in(); // true $has_session = Session::has_session(); // true CLI mode automatically detects when running via php_sapi_name() === 'cli' and requires external code to explicitly set user_id/site_id. BASIC USAGE Check Authentication: use App\RSpade\Core\Session\Session; // Check if user is logged in if (Session::is_logged_in()) { $user = Session::get_user(); $user_id = Session::get_user_id(); } // Check if session exists at all if (Session::has_session()) { // Session record exists or CLI values set } Login User: // Option 1: Pass user ID Session::set_user_id(123); // Option 2: Pass user model $user = User_Model::find(123); Session::set_user($user); // Both options: // - Create session if none exists // - Regenerate session token (prevent session fixation) // - Update user's last_login timestamp // - Set secure cookie Logout: Session::logout(); // OR Session::set_user(null); Site Management: // Get current site $site_id = Session::get_site_id(); // Returns 0 if not set $site = Session::get_site(); // Returns Site_Model or null // Set site Session::set_site_id(456); // OR Session::set_site($site_model); CSRF Protection: // Get CSRF token for forms $token = Session::get_csrf_token(); // Verify submitted token if (Session::verify_csrf_token($submitted_token)) { // Token is valid } API REFERENCE Authentication Methods: Session::is_logged_in(): bool Returns true if user_id is set, false otherwise. Does NOT create session if none exists. Session::has_session(): bool Returns true if session exists (web mode) or if site_id/user_id set (CLI mode). Does NOT create session if none exists. User Methods: Session::get_user_id(): int|null Returns current user ID or null. Does NOT create session. Session::get_user(): User_Model|null Returns User_Model for current user or null. Caches result. Does NOT create session. Session::set_user(User_Model|int|null $user): void Set current user. Pass null or 0 to logout. CREATES session if none exists. Regenerates session token on login (security). Session::set_user_id(int|null $user_id): void Convenience method, same as set_user(). CREATES session if none exists. Session::logout(): void Clears user_id from session. Same as set_user(null). Site Methods: Session::get_site_id(): int Returns current site ID or 0. Does NOT create session. Session::get_site(): Site_Model|null Returns Site_Model for current site or null. Caches result. Does NOT create session. Session::set_site(Site_Model|int $site): void Set current site ID. CREATES session if none exists. Session::set_site_id(int $site_id): void Convenience method, same as set_site(). CREATES session if none exists. Site User Methods: Session::get_site_user(): Site_User_Model|null Returns Site_User_Model matching both current user_id and site_id, or null if either is not set or no matching record exists. Does NOT create session. Session Access Methods: Session::get_session_id(): int Returns session record ID. CREATES session if none exists. Session::get_session(): Session Returns Session model. CREATES session if none exists. CSRF Methods: Session::get_csrf_token(): string|null Returns CSRF token for current session or null. Does NOT create session. Session::verify_csrf_token(string $token): bool Verifies submitted CSRF token using constant-time comparison. Returns false if no session exists. Administrative Methods: Session::find_by_token(string $token): Session|null Find active session by token (for API/external access). Session::cleanup_expired(int $days = 365): int Delete sessions older than specified days. Returns count deleted. Run periodically via scheduled command. Session::reset(): void Logout and clear all session data. Marks session inactive. Clears cookie. CONTROLLER AUTHENTICATION Using pre_dispatch: class Admin_Controller extends Rsx_Controller_Abstract { public static function pre_dispatch(Request $request, array $params = []) { // Require authentication for all admin routes if (!Session::is_logged_in()) { return redirect('/login'); } // Require specific user role $user = Session::get_user(); if (!$user || $user->role !== 'admin') { return redirect('/'); } return null; // Continue to route } } Application-wide Authentication: In rsx/main.php: public static function pre_dispatch(Request $request, array $params = []) { // Require auth for /app/* routes if (str_starts_with($request->path(), 'app/')) { if (!Session::is_logged_in()) { return redirect('/login'); } } return null; } SECURITY Automatic Security Features: - Session tokens: 64-character cryptographically secure random - Token regeneration on login prevents session fixation - Constant-time token comparison prevents timing attacks - Secure cookie flags: httponly, secure, samesite=Lax - CSRF tokens generated automatically - 365-day expiration with last_active tracking Cookie Settings: - Name: rsx_session - HttpOnly: true (no JavaScript access) - Secure: true (HTTPS only) - SameSite: Lax (CSRF protection) - Path: / - Expires: 365 days from last activity Session Fixation Prevention: Session tokens are regenerated when: - User logs in (set_user called with non-null value) This prevents attackers from forcing a victim to use a known session token. EXAMPLES Login Form Processing: #[Route('/login')] public static function login(Request $request, array $params = []) { if ($request->method() === 'POST') { $email = $request->input('email'); $password = $request->input('password'); $user = User_Model::where('email', $email)->first(); if ($user && password_verify($password, $user->password)) { // Login successful - this creates/regenerates session Session::set_user($user); Rsx::flash_success('Login successful!'); return redirect('/dashboard'); } Rsx::flash_error('Invalid credentials'); return redirect('/login'); } return view('auth/login'); } Require Authentication: public static function dashboard(Request $request, array $params = []) { // Check authentication at start of method if (!Session::is_logged_in()) { Rsx::flash_error('Please login to continue'); return redirect('/login'); } $user = Session::get_user(); return view('dashboard/index', [ 'user' => $user, ]); } Multi-tenant Site Selection: public static function select_site(Request $request, array $params = []) { $site_id = $params['site_id'] ?? null; // Verify user has access to this site $site_user = Site_User_Model::where('user_id', Session::get_user_id()) ->where('site_id', $site_id) ->first(); if (!$site_user) { Rsx::flash_error('Access denied to this site'); return redirect('/sites'); } // Set active site Session::set_site_id($site_id); Rsx::flash_success('Switched to ' . $site_user->site->name); return redirect('/dashboard'); } CSRF Protection in Forms: In controller: return view('forms/example', [ 'csrf_token' => Session::get_csrf_token(), ]); In Blade view:
In POST handler: $submitted = $request->input('csrf_token'); if (!Session::verify_csrf_token($submitted)) { Rsx::flash_error('Invalid CSRF token'); return redirect()->back(); } CLI Command with User Context: namespace App\Console\Commands; use Illuminate\Console\Command; use App\RSpade\Core\Session\Session; class ProcessUserData extends Command { protected $signature = 'data:process {user_id}'; public function handle() { $user_id = $this->argument('user_id'); // Set CLI session context Session::set_user_id($user_id); // Now all code that checks Session::get_user_id() will work $this->process_data(); $this->info('Processed data for user ' . $user_id); } private function process_data() { // This works because we set user_id above $user_id = Session::get_user_id(); // ... process data for user ... } } RSX VS LARAVEL Session Access: Laravel: session()->put('key', 'value'); $value = session()->get('key'); session()->forget('key'); RSX: Session::set_user_id(123); $user_id = Session::get_user_id(); Session::logout(); RSX uses typed methods instead of generic key/value storage. Authentication: Laravel: Auth::login($user); Auth::logout(); $user = Auth::user(); $id = Auth::id(); if (Auth::check()) { } RSX: Session::set_user($user); Session::logout(); $user = Session::get_user(); $id = Session::get_user_id(); if (Session::is_logged_in()) { } RSX combines session and auth into one unified interface. Session Creation: Laravel: Session starts automatically on every request via middleware. RSX: Session only created when explicitly needed via set_user(), set_site(), get_session_id(), or get_session(). Getter methods like get_user_id() do NOT create sessions. This prevents unnecessary database writes for unauthenticated traffic. CSRF Protection: Laravel: @csrf in Blade templates $request->session()->token() Automatic verification via middleware RSX: Session::get_csrf_token() in controllers Session::verify_csrf_token($token) manual verification Manual implementation gives explicit control Remember Me: Laravel: Auth::login($user, $remember = true); Optional remember functionality RSX: All sessions are persistent (365 days) No "remember me" checkbox needed TROUBLESHOOTING Session Not Persisting Across Requests: Problem: Session data is lost between requests Solutions: - Check that cookies are enabled in browser - Verify HTTPS is being used (secure flag requires HTTPS) - Check session token in cookie: rsx_session - Verify session record exists in database - Check last_active timestamp is being updated User Logged Out Unexpectedly: Problem: User is logged out without calling logout() Solutions: - Check session expiration (365 days by default) - Verify session.active = true in database - Check for code calling Session::reset() or Session::logout() - Verify session token hasn't changed CSRF Token Validation Failing: Problem: verify_csrf_token() returns false Solutions: - Ensure token is being submitted in POST data - Check session exists (get_csrf_token returns null if no session) - Verify token wasn't regenerated between form display and submit - Check for session fixation prevention regenerating token CLI Commands Not Working: Problem: Session methods return null/0 in artisan commands Solution: - Explicitly set user_id/site_id in CLI context: Session::set_user_id($user_id); Session::set_site_id($site_id); - CLI mode requires manual context setting, does not load from DB Session Created for Anonymous Users: Problem: Every visitor gets a session record Solution: - Only call methods that CREATE sessions when needed: CREATES: set_user(), set_site(), get_session_id(), get_session() NO CREATE: get_user_id(), get_site_id(), is_logged_in() - Don't call get_session_id() or get_session() in templates/layouts - Use has_session() to check without creating GARBAGE COLLECTION Automatic Session Cleanup: Sessions are automatically cleaned up via scheduled task that runs daily at 3 AM. No manual configuration required. Cleanup Rules: - Logged-in sessions (login_user_id set): Deleted after 365 days - Anonymous sessions (login_user_id null): Deleted after 14 days The cleanup uses last_active timestamp to determine session age. Sessions are permanently deleted from the database. Implementation: Service: Session_Cleanup_Service::cleanup_sessions() Schedule: Daily at 3 AM (via #[Schedule] attribute) Automatic: No cron configuration needed Manual Trigger: To manually run cleanup outside the schedule: php artisan rsx:task:run Session_Cleanup_Service cleanup_sessions This is useful for: - Testing cleanup logic - Emergency cleanup when disk space is low - One-time cleanup after changing retention policies SEE ALSO rsx:man routing - Type-safe URL generation rsx:man model_fetch - Ajax ORM with security rsx:man rsx_architecture - RSX application structure