From 9a0c0145ff8107ec7055eaf9489fb309c6b16a94 Mon Sep 17 00:00:00 2001 From: Randall Wilk Date: Mon, 14 Oct 2024 13:52:18 -0500 Subject: [PATCH] Update docs --- docs/advanced-usage/mfa.md | 13 +++- docs/advanced-usage/sudo-mode.md | 115 ++++++++++++++++++++++++------- docs/components/_index.md | 4 -- docs/components/masked-values.md | 52 -------------- docs/pages/profile.md | 14 ++-- docs/pages/security.md | 2 +- docs/requirements.md | 4 +- 7 files changed, 117 insertions(+), 87 deletions(-) delete mode 100644 docs/components/_index.md delete mode 100644 docs/components/masked-values.md diff --git a/docs/advanced-usage/mfa.md b/docs/advanced-usage/mfa.md index 60eb545..2df2c2b 100644 --- a/docs/advanced-usage/mfa.md +++ b/docs/advanced-usage/mfa.md @@ -48,7 +48,7 @@ Our macro also accepts parameters to customize the url paths, as well as the mid Route::webauthn( prefix: 'sessions/webauthn', assertionMiddleware: [\Illuminate\Routing\Middleware\ValidateSignature::class], - attestationMiddleware: [\Filament\Http\Middleware\Authenticate::class], + attestationMiddleware: [\Illuminate\Auth\Middleware\Authenticate::class], ); // Wrapping in your own group @@ -383,6 +383,17 @@ Now just create the view being referenced in the render hook:
{{ $this->passkeyLoginAction }}
``` +### Custom Passkey Auth Flow + +If you need more control over how the user is authenticated when passkey login is used, you may define a custom callback using `authenticateUsing` on the passkey login action. The action will provide the passkey and the `publicKeyCredentialSource` object that was obtained from verifying the webauthn assertion. From there, you can log your user in and handle the redirect yourself. + +```php +PasskeyLoginAction::make() + ->authenticateUsing(function (WebauthnKey $passkey, PublicKeyCredentialSource $publicKeyCredentialSource) { + // Handle login and redirect here. + }); +``` + ## Preferred Mfa Method By default, we will use a user's first available mfa method registered them as their "preferred" method for authentication. This means that it will be the first method shown on the mfa challenge screen for the user. diff --git a/docs/advanced-usage/sudo-mode.md b/docs/advanced-usage/sudo-mode.md index e36ecc6..3fd9fcd 100644 --- a/docs/advanced-usage/sudo-mode.md +++ b/docs/advanced-usage/sudo-mode.md @@ -48,51 +48,120 @@ When this challenge is shown to the user, we will dispatch the `SudoModeChalleng ### Use Sudo Challenge Action -To show a sudo challenge for your own sensitive actions, you need to do the following: +To show a sudo challenge for your own sensitive actions, you have two options: create a custom action class, or include a trait in a livewire component to enforce sudo mode for you. Below is a basic overview of how to implement each strategy: -1. Use the `UsesSudoChallengeAction` trait on your livewire component: +#### Custom Action Class + +With this strategy, a custom filament action will check for and enforce sudo mode before its action is executed. This is typically what you'll want to reach for when requiring sudo mode for actions. + +1. First, create a new filament action and pull the `RequiresSudo` trait on the class. Here is a basic example of what you'll need to include inside of your action's `setup` method: ```php -use Rawilk\ProfileFilament\Concerns\Sudo\UsesSudoChallengeAction; +use Filament\Actions\Action; use Livewire\Component; -class YourComponent extends Component +class SensitiveAction extends Action { - use UsesSudoChallengeAction; + use RequiresSudo; + + protected function setUp(): void + { + parent::setUp(); + + $this->before(function (Component $livewire) { + $this->ensureSudoIsActive($livewire); + }); + + $this->mountUsing(function (Component $livewire) { + $this->mountSudoAction($livewire); + }); + + $this->registerModalActions([ + $this->getSudoChallengeAction(), + ]); + } } ``` -2. Enforce sudo mode in the `mountUsing` function on your action. +> {tip} You should only need to include the `before` hook if your action requires confirmation. This is to ensure sudo mode is still active in the event of the user entering sudo mode but waiting too long to perform the action. + +2. With the sensitive action defined, you just need to define and use it like you normally would in your livewire component: ```php -public function sensitiveAction(): Action +use Filament\Actions\Action; +use Livewire\Component; + +class YourComponent extends Component { - return Action::make('sensitiveActionName') - // ... - ->mountUsing(function () { - $this->ensureSudoIsActive(returnAction: 'sensitiveActionName'); - }); + // ... + + public function sensitiveAction(): Action + { + return SensitiveAction::make(); + } } ``` -Make sure the `returnAction` parameter is the name of your action name, so it can be re-mounted by our action when sudo mode is entered. +> {tip} This will also work with infolist and table actions as well. The `RequiresSudo` trait will handle everything for you. + +#### From Livewire Component + +In cases where the user needs to perform a sensitive action but a filament action class doesn't make sense, you may check for and enforce sudo mode directly from your livewire component. + +1. Include the `SudoChallengeForm` livewire component in your component's markup. + +```html +
+ + + @livewire(\Rawilk\ProfileFilament\Livewire\Sudo\SudoChallengeForm::class) +
+``` + +> {note} If you have multiple components on the same page that will check for sudo mode this way, you should include our livewire component on the page itself, outside your livewire component definitions. -You can optionally check that sudo mode is still active in your `action`, in the case the user entered sudo mode, but left the page idle long enough for sudo mode to expire. +2. Use the `UsesSudoChallengeAction` trait on your livewire component: ```php -public function sensitiveAction(): Action +use Livewire\Component; +use Rawilk\ProfileFilament\Concerns\Sudo\UsesSudoChallengeAction; + +class YourComponent extends Component { - return Action::make('sensitiveActionName') - // ... - ->action(function () { - $this->ensureSudoIsActive(returnAction: 'sensitiveActionName'); - }) - ->mountUsing(function () { - // ... - }); + use UsesSudoChallengeAction; +} +``` + +3. Enforce sudo mode by calling `$this->ensureSudoIsActive()` in your action method. You should also listen for the `sudo-active` livewire event on your method as well to continue processing once the user has entered sudo mode. + +```php +use Livewire\Attributes\On; + +#[On('sudo-active')] +public function sensitiveAction(): void +{ + if (! $this->ensureSudoIsActive()) { + return; + } + + // Sudo is active, continue processing. } ``` +The `ensureSudoIsActive` method in our trait will dispatch an event to our `SudoChallengeForm`, which will take care of enforcing sudo mode for you. Once sudo mode has been entered, our component will dispatch the `sudo-active` event, which you should listen to as shown above. + +If you have multiple sensitive actions in the same component, you can pass the method name as an argument to `ensureSudoIsActive`, which will then be included in the payload of the `sudo-active` event once it is dispatched. + +```php +$this->ensureSudoIsActive(method: 'mySensitiveAction'); +``` + +If you have data that you will need to access across requests, you can include that as an argument to the `ensureSudoIsActive` method as well. + +```php +$this->ensureSudoIsActive(data: ['foo' => 'bar']); +``` + ## Middleware To protect sensitive routes, you can apply the `RequiresSudoMode` middleware to your route. If sudo mode needs to be challenged, the middleware will redirect to a full-page sudo challenge. This challenge page looks and behaves exactly like the sudo challenge modal does. diff --git a/docs/components/_index.md b/docs/components/_index.md deleted file mode 100644 index 2dde279..0000000 --- a/docs/components/_index.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Components -sort: 3 ---- diff --git a/docs/components/masked-values.md b/docs/components/masked-values.md deleted file mode 100644 index 250d8af..0000000 --- a/docs/components/masked-values.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: Masked Values -sort: 1 ---- - -## Introduction - -There may be situations where you need to show a sensitive piece of data on the page, but initially hide it from the user. This where our `MaskedEntry` info list item comes in. It will initially blur the value, and then once clicked on it will reveal the actual value it's hiding. - -Here's a screenshot of the entry being used to initially hide a user's ID on their profile page: - -![masked entry](https://github.com/rawilk/profile-filament-plugin/blob/main/assets/images/masked-entry.png?raw=true) - -## Usage - -In your component's info list schema, you can add the `MaskedEntry` for a model's field like this: - -```php -use Rawilk\ProfileFilament\Filament\Infolists\Components\MaskedEntry; -use Illuminate\Support\Str; - -protected function infolistSchema(): array -{ - return [ - // ... - MaskedEntry::make('id') - ->label('User id') - ->copyable() - ]; -} -``` - -## Mask Using - -By default, we will use Laravel's `Str::mask()` helper to render the masked value as all '\*' characters underneath the blur. This is for security, so the actual value cannot be inspected underneath the blur with element inspector. - -If you want to customize how the value is masked, you may provide a callback to `maskUsing`. This is useful if you want the value to look a little more realistic underneath the blur. - -```php -MaskedEntry::make('id') - ->maskUsing(fn ($state): string => 'usr_' . Str::mask($state, '*')) -``` - -## Sudo Confirmation - -If you want your user to have to enter [sudo mode](/docs/profile-filament-plugin/{version}/advanced-usage/sudo) before they can reveal the value, you can require sudo mode like this: - -```php -MaskedEntry::make('id') - // ... - ->requireSudoConfirmation() -``` diff --git a/docs/pages/profile.md b/docs/pages/profile.md index 0039e67..32fea8a 100644 --- a/docs/pages/profile.md +++ b/docs/pages/profile.md @@ -29,7 +29,7 @@ To accomplish this, the two main methods we need to override are the `infolistSc namespace App\Livewire; use Filament\Forms\Components\Select; -use Filament\Infolists\Components\TextEntry; +use Filament\Infolists\Components\Section;use Filament\Infolists\Components\TextEntry; use Rawilk\ProfileFilament\Livewire\Profile\ProfileInfo; class CustomProfileInfo extends ProfileInfo @@ -37,9 +37,15 @@ class CustomProfileInfo extends ProfileInfo protected function infolistSchema(): array { return [ - $this->nameTextEntry(), - TextEntry::make('timezone'), - $this->createdAtTextEntry(), + Section::make('Your information') + ->headerActions([ + $this->editAction(), + ]) + ->schema([ + $this->nameTextEntry(), + TextEntry::make('timezone'), + $this->createdAtTextEntry(), + ]) ]; } diff --git a/docs/pages/security.md b/docs/pages/security.md index 8f071d4..7587b46 100644 --- a/docs/pages/security.md +++ b/docs/pages/security.md @@ -116,7 +116,7 @@ Authenticator apps are used to generate one-time passwords (totp) that are used When an authenticator app is successfully registered, we will dispatch the `TwoFactorAppAdded` event from the `ConfirmTwoFactorAppAction`, which will receive the user and new authenticator app model instance. You may choose to listen for this event to alert a user when a new totp app is registered on their account. -We will also mark mfa as enable on the user if it isn't already, and then also generate a set of [Recovery Codes](#user-content-recovery-codes) for the user. +We will also mark mfa as enabled on the user if it isn't already, and then also generate a set of [Recovery Codes](#user-content-recovery-codes) for the user. #### Customize Confirm Action diff --git a/docs/requirements.md b/docs/requirements.md index 261dcbf..834ec0c 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -6,8 +6,8 @@ sort: 2 ## General Requirements - PHP **8.2** or greater -- Laravel **10.0** or greater -- Filament **3.2** or greater +- Laravel **11.23** or greater +- Filament **3.2.96** or greater ## Session Management