68 lines
1.7 KiB
Markdown
68 lines
1.7 KiB
Markdown
|
|
# 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();
|
||
|
|
```
|