'boolean', 'sms_sent' => 'boolean', 'source' => 'integer', ]; // Login OTP requested — an SMS was queued (unless the admin shortcut mobile) public const EVENT_OTP_SENT = 'otp_sent'; // Login OTP checked successfully — the user is now logged in public const EVENT_OTP_VERIFIED = 'otp_verified'; // Login OTP checked with a wrong/expired token public const EVENT_OTP_FAILED = 'otp_failed'; // In-app mobile verification OTP requested — also a billed SMS public const EVENT_MOBILE_OTP_SENT = 'mobile_otp_sent'; public const EVENT_MOBILE_VERIFIED = 'mobile_verified'; public const EVENT_MOBILE_VERIFY_FAILED = 'mobile_verify_failed'; // Password login public const EVENT_LOGIN = 'login'; public const EVENT_LOGIN_FAILED = 'login_failed'; public const EVENT_REGISTER = 'register'; public const EVENT_GOOGLE_LOGIN = 'google_login'; public const EVENTS = [ self::EVENT_OTP_SENT, self::EVENT_OTP_VERIFIED, self::EVENT_OTP_FAILED, self::EVENT_MOBILE_OTP_SENT, self::EVENT_MOBILE_VERIFIED, self::EVENT_MOBILE_VERIFY_FAILED, self::EVENT_LOGIN, self::EVENT_LOGIN_FAILED, self::EVENT_REGISTER, self::EVENT_GOOGLE_LOGIN, ]; // Events that cost an outgoing verification SMS public const SMS_EVENTS = [ self::EVENT_OTP_SENT, self::EVENT_MOBILE_OTP_SENT, ]; /** * Record an event. Never lets a logging failure break the auth flow. */ public static function record(string $event, array $attributes = [], ?Request $request = null): ?self { try { $request ??= request(); return static::create(array_merge([ 'event' => $event, 'success' => !str_ends_with($event, '_failed'), 'sms_sent' => in_array($event, self::SMS_EVENTS, true), 'ip' => $request?->ip(), 'user_agent' => substr((string) $request?->userAgent(), 0, 255) ?: null, ], $attributes)); } catch (\Throwable $e) { logger()->warning('auth log failed', ['event' => $event, 'error' => $e->getMessage()]); return null; } } public function user() { return $this->belongsTo(User::class); } protected function serializeDate(DateTimeInterface $date) { return $date->format('Y-m-d H:i:s'); } }