91 lines
2.8 KiB
PHP
91 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use DateTimeInterface;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Http\Request;
|
|
|
|
/**
|
|
* One row per authentication event. Used by the admin panel to count how many
|
|
* users request/verify OTPs (i.e. how many verification SMS are billed) and how
|
|
* many log in through each method.
|
|
*/
|
|
class AuthLog extends Model
|
|
{
|
|
protected $guarded = [];
|
|
|
|
protected $casts = [
|
|
'success' => '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');
|
|
}
|
|
}
|