package models import ( "time" "github.com/google/uuid" ) // Session represents an authenticated user session in the system. // A session is created when a user logs in and tracks their authentication state. // // What is a session? // - Created during login // - Associated with a refresh token // - Tracks device and location information // - Can be individually revoked // - Has an expiration date // // Why track sessions? // 1. Security: See all active logins // 2. Control: Revoke specific sessions ("logout from my phone") // 3. Audit: Track when/where users logged in // 4. Device management: Show users their active devices // 5. Token validation: Verify refresh tokens haven't been revoked // // Session lifecycle: // 1. Created: When user logs in // 2. Active: Used to refresh access tokens // 3. Last used updated: Every time refresh token is used // 4. Expired: When expires_at passes // 5. Revoked: When user logs out or admin revokes // 6. Deleted: Cleanup job removes old expired/revoked sessions // // Security model: // - Refresh token hash stored (not plaintext) // - Session can be revoked (logout) // - All sessions can be revoked (password change) // - Tracks device/location for anomaly detection type Session struct { // ID is the unique identifier for this session // Type: UUID v4 (128-bit random identifier) // Generated: Automatically by database on insert // Used for: Looking up sessions, revoking specific sessions ID uuid.UUID `json:"id" db:"id"` // UserID identifies which user this session belongs to // Type: UUID v4 (foreign key to users table) // Used for: Finding all sessions for a user, revoking all user sessions // Relationship: Many sessions can belong to one user UserID uuid.UUID `json:"user_id" db:"user_id"` // RefreshTokenHash is the hashed version of the refresh token // Type: String (base64-encoded SHA-256 hash) // Security: NEVER expose this in API responses (hence json tag comment) // Why hashed: If database breached, tokens can't be used // Hashing: SHA-256 (deterministic, allows lookup) // Storage: Should be excluded from JSON in production/staging RefreshTokenHash string `json:"refresh_token_hash" db:"refresh_token_hash"` // Never expose in JSON in (prod, staging) // UserAgent contains the browser/application identifier // Type: Optional string (can be null) // Example: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124" // Used for: Displaying device information to user // Privacy: Contains OS and browser info (PII consideration) UserAgent *string `json:"user_agent" db:"user_agent"` // IPAddress is the IP address from which the session was created // Type: Optional string (can be null, IPv4 or IPv6) // Example: "192.168.1.1" or "2001:0db8:85a3:0000:0000:8a2e:0370:7334" // Used for: Location display, anomaly detection // Privacy: PII under GDPR, may need anonymization after time // Note: Could be proxy IP if behind load balancer IPAddress *string `json:"ip_address" db:"ip_address"` // DeviceName is an optional user-friendly name for the device // Type: Optional string (can be null) // Example: "John's iPhone", "Work Laptop", "Home PC" // Used for: User convenience (easier to identify devices) // Set by: Client can optionally provide this DeviceName *string `json:"device_name" db:"device_name"` // DeviceType categorizes the type of device // Type: String (enum-like values) // Possible values: "mobile", "desktop", "web", "unknown" // Detection: Based on user agent string parsing // Used for: Filtering sessions, displaying appropriate icons DeviceType string `json:"device_type" db:"device_type"` // ExpiresAt is when this session expires // Type: Timestamp (typically 7 days from creation) // After expiry: Session cannot be used to get new access tokens // Cleanup: Expired sessions eventually deleted by cleanup job // Database check: Queries filter out expired sessions ExpiresAt time.Time `json:"expires_at" db:"expires_at"` // IsRevoked indicates if session has been explicitly invalidated // Type: Boolean (default false) // Set to true when: User logs out, password changed, admin action // Effect: Refresh token can no longer be used // Database: Indexed for faster queries IsRevoked bool `json:"is_revoked" db:"is_revoked"` // RevokedAt is when the session was revoked // Type: Optional timestamp (null if not revoked) // Set when: IsRevoked changed to true // Used for: Audit trail, analytics RevokedAt *time.Time `json:"revoked_at" db:"revoked_at"` // RevokedReason explains why the session was revoked // Type: Optional string (null if not revoked) // Common values: "user_logout", "password_change", "admin_action", "security_breach" // Used for: Audit trail, understanding logout patterns, security investigations RevokedReason *string `json:"revoked_reason" db:"revoked_reason"` // CreatedAt is when the session was created (login time) // Type: Timestamp (automatically set by database) // Used for: Displaying "logged in" time, sorting sessions // Database: Set by DEFAULT NOW() in schema CreatedAt time.Time `json:"created_at" db:"created_at"` // LastUsedAt is when the refresh token was last used // Type: Timestamp (updated on each token refresh) // Used for: Showing session activity, identifying stale sessions // Updated: Every ~15 minutes when access token is refreshed // Cleanup: Can remove sessions not used in X days LastUsedAt time.Time `json:"last_used_at" db:"last_used_at"` } // CreateSessionInput contains the data needed to create a new session. // This is a DTO (Data Transfer Object) used to pass data to the repository. // // Why separate input struct? // - Separates what client provides from what database generates // - Clear interface for session creation // - Database generates ID and timestamps // - Type safety (can't accidentally set ID or timestamps) // // When used: // - During login (user authentication) // - When creating refresh token type CreateSessionInput struct { // UserID identifies which user this session belongs to // Required: Must be valid user ID UserID uuid.UUID // RefreshToken is the plaintext token to be hashed // Security: Will be hashed before storage (SHA-256) // Never stored plaintext in database // Generated: Random 32-byte value, base64 encoded RefreshToken string // UserAgent is optional browser/app information // Extracted from: HTTP User-Agent header // Can be nil: If header not provided UserAgent *string // IPAddress is optional IP address of request // Extracted from: X-Forwarded-For or RemoteAddr // Can be nil: If not available IPAddress *string // DeviceName is optional user-friendly device name // Provided by: Client (optional) // Can be nil: Most often not provided DeviceName *string // DeviceType categorizes the device // Detected from: User agent string // Values: "mobile", "desktop", "web", "unknown" // Required: Always set (uses "unknown" if can't detect) DeviceType string // ExpiresAt is when the session should expire // Calculated: Now + RefreshExpiry (typically 7 days) // Required: Must be set ExpiresAt time.Time }