Files

68 lines
1.7 KiB
Markdown
Raw Permalink Normal View History

2026-07-01 18:35:39 +03:00
# OTP Authentication
Staff authentication uses a passwordless OTP flow. No password is stored or required.
---
## Flow
1. Staff enters their email on the login page
2. A 6-digit code is generated, stored on the `staff` record, and emailed
3. Staff enters the code — if valid and not expired, they are logged in
---
## Implementation
### OtpService
`Modules\Core\Services\OtpService` handles generation and validation.
```php
// Generate and email a code
$otpService->generateAndSend(string $email): bool
// Validate a submitted code — returns the Staff model on success, null on failure
$otpService->validate(string $email, string $code): ?Staff
```
Codes expire after **10 minutes**. After a successful validation the code is cleared from the record.
### Database
Two columns on the `lunar_staff` table (added by `2026_05_06_000001_add_otp_to_lunar_staff_table`):
| Column | Type | Purpose |
|---|---|---|
| `otp_code` | string, nullable | The generated code |
| `otp_expires_at` | timestamp, nullable | Expiry time |
### Login Page
`Modules\Core\Filament\Pages\Login` is a Filament `SimplePage` registered as the panel login via `AppServiceProvider`:
```php
LunarPanel::panel(fn (Panel $panel) => $panel->login(Login::class));
```
It has two steps rendered in `core::filament.pages.login`:
- **Step 1** — email form, submits to `requestOtp()`
- **Step 2** — code form, submits to `authenticate()`
After a valid code `canAccessPanel()` is checked before the session is established.
### Email
`Modules\Core\Mail\OtpMail` sends the code using the `core::mail.otp` view.
---
## Disabling 2FA
Lunar's built-in 2FA plugin is disabled in `AppServiceProvider` since the OTP login replaces it:
```php
LunarPanel::disableTwoFactorAuth();
```