Skip to content

Commit

Permalink
feat: Support 3
Browse files Browse the repository at this point in the history
  • Loading branch information
lee-to committed Oct 15, 2024
1 parent bf22a80 commit 143756e
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 126 deletions.
40 changes: 37 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

### Requirements

- MoonShine v2.4.0+
- MoonShine v3.0+

### Support MoonShine versions

| MoonShine | Layouts |
|-------------|---------|
| 2.0+ | 1.0+ |
| 3.0+ | 2.0+ |

### Installation

Expand Down Expand Up @@ -32,6 +39,33 @@ return [
]
// ...
];
```

or in `MoonShineServiceProvider`

```php
<?php

declare(strict_types=1);

namespace App\Providers;

use MoonShine\Laravel\DependencyInjection\MoonShineConfigurator;
use MoonShine\Laravel\Providers\MoonShineApplicationServiceProvider;
use MoonShine\TwoFactor\TwoFactorAuthPipe;

class MoonShineServiceProvider extends MoonShineApplicationServiceProvider
{
// ...

protected function configure(MoonShineConfigurator $config): MoonShineConfigurator
{
return $config->authPipelines([
TwoFactorAuthPipe::class
]);
}
}

```
Add trait TwoFactorAuthenticatable to model or use MoonShine\TwoFactor\Models\MoonshineUser

Expand All @@ -44,12 +78,12 @@ class MoonshineUser extends Model
}
```

Add component to ProfilePage
We will automatically add the component to the profile page, but if you use another page, you can add it yourself.

```php
use MoonShine\TwoFactor\ComponentSets\TwoFactor;

protected function components(): array
protected function components(): iterable
{
return [
// ...
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
}
],
"require": {
"php": "^8.1|^8.2|^8.3",
"php": "^8.2|^8.3",
"ext-curl": "*",
"ext-json": "*",
"bacon/bacon-qr-code": "^2.0",
Expand All @@ -41,7 +41,7 @@
}
},
"conflict": {
"moonshine/moonshine": "<2.4"
"moonshine/moonshine": "<3.0"
},
"scripts": {
"test": "vendor/bin/phpunit",
Expand Down
24 changes: 0 additions & 24 deletions resources/views/login/two-factor-challenge.blade.php

This file was deleted.

28 changes: 10 additions & 18 deletions routes/two-factor.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
<?php

use Illuminate\Support\Facades\Route;
use MoonShine\Pages\ViewPage;
use MoonShine\TwoFactor\Forms\ChallengeForm;
use MoonShine\TwoFactor\Http\Controllers\TwoFactorController;
use MoonShine\TwoFactor\Pages\ChallengePage;

Route::prefix(config('moonshine.route.prefix', ''))
->middleware('moonshine')
->as('moonshine-two-factor.')->group(static function (): void {
Route::middleware(config('moonshine.auth.middleware', []))
->prefix('two-factor')
Route::moonshine(static function (): void {
Route::as('moonshine-two-factor.')
->prefix('t/f/two-factor')
->controller(TwoFactorController::class)
->group(function (): void {

Expand Down Expand Up @@ -37,16 +34,11 @@
'check',
'check',
)->name('check')
->withoutMiddleware(config('moonshine.auth.middleware', []));

Route::get('challenge', static function () {
return ViewPage::make()
->setLayout('moonshine::layouts.login')
->setContentView(
'moonshine-two-factor::login.two-factor-challenge',
['form' => ChallengeForm::make()]
);
->withoutMiddleware(moonshineConfig()->getAuthMiddleware());

Route::get('challenge', static function (ChallengePage $page) {
return $page;
})->name('challenge')
->withoutMiddleware(config('moonshine.auth.middleware', []));
->withoutMiddleware(moonshineConfig()->getAuthMiddleware());
});
});
}, withAuthenticate: true);
95 changes: 43 additions & 52 deletions src/ComponentSets/TwoFactor.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,29 @@
namespace MoonShine\TwoFactor\ComponentSets;

use Closure;
use MoonShine\ActionButtons\ActionButton;
use MoonShine\Components\FlexibleRender;
use MoonShine\Components\FormBuilder;
use MoonShine\Components\MoonShineComponent;
use MoonShine\Components\When;
use MoonShine\Decorations\Block;
use MoonShine\Decorations\Fragment;
use MoonShine\Decorations\LineBreak;
use MoonShine\Exceptions\DecorationException;
use MoonShine\Exceptions\PageException;
use MoonShine\Fields\Password;
use MoonShine\Fields\Text;
use MoonShine\Laravel\Components\Fragment;
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\HttpMethod;
use MoonShine\Support\Enums\JsEvent;
use MoonShine\UI\Components\ActionButton;
use MoonShine\UI\Components\Components;
use MoonShine\UI\Components\FlexibleRender;
use MoonShine\UI\Components\FormBuilder;
use MoonShine\UI\Components\Layout\Box;
use MoonShine\UI\Components\Layout\LineBreak;
use MoonShine\UI\Components\MoonShineComponent;
use MoonShine\UI\Components\When;
use MoonShine\UI\Fields\Password;
use MoonShine\UI\Fields\Text;

final class TwoFactor
{
/**
* @throws DecorationException
* @throws PageException
*/
public static function make(): MoonShineComponent
{
return (new self())->twoFactorBlock();
return Components::make([
LineBreak::make(),
(new self())->twoFactorBlock()
]);
}

private function twoFactorWithConfirm(): bool
Expand All @@ -39,13 +40,9 @@ private function twoFactorEnabled(): bool
return config('two-factor.enable', true);
}

/**
* @throws DecorationException
* @throws PageException
*/
public function twoFactorBlock(): MoonShineComponent
{
return $this->twoFactorEnabled() ? Block::make(__('moonshine-two-factor::ui.2fa'), [
return $this->twoFactorEnabled() ? Box::make(__('moonshine-two-factor::ui.2fa'), [
$this->enableDisableTwoFactor(),

LineBreak::make(),
Expand All @@ -58,10 +55,6 @@ public function twoFactorBlock(): MoonShineComponent
]) : LineBreak::make();
}

/**
* @throws DecorationException
* @throws PageException
*/
protected function enableDisableTwoFactor(): MoonShineComponent
{
$label = request('enable-disable')
Expand All @@ -76,49 +69,49 @@ protected function enableDisableTwoFactor(): MoonShineComponent
['button-clicked-enable'],
fn () => ActionButton::make(
$label,
route('moonshine-two-factor.enable')
route('moonshine.moonshine-two-factor.enable')
)
->customAttributes([
'style' => $this->twoFactorWithConfirm() ? 'display: none;' : '',
'x-on:button-clicked-enable.window' => 'request',
])
->async('POST', events: ['fragment-updated-qr-code', 'fragment-updated-enable-disable'])
->async(HttpMethod::POST, events: [
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'qr-code'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'enable-disable'),
])
),
fn () => $this->confirmAction(
__('moonshine-two-factor::ui.disable'),
['button-clicked-disable'],
fn () => ActionButton::make(
__('moonshine-two-factor::ui.disable'),
route('moonshine-two-factor.disable')
route('moonshine.moonshine-two-factor.disable')
)
->customAttributes([
'style' => $this->twoFactorWithConfirm() ? 'display: none;' : '',
'x-on:button-clicked-disable.window' => 'request',
])
->async('DELETE', events: [
'fragment-updated-qr-code',
'fragment-updated-recovery-code',
'fragment-updated-enable-disable',
->async(HttpMethod::DELETE, events: [
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'qr-code'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'recovery-code'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'enable-disable'),
])
)
),
])->name('enable-disable')->updateAsync(['enable-disable' => is_null(auth()->user()->two_factor_confirmed_at)]);
])->name('enable-disable')->updateWith(['enable-disable' => is_null(auth()->user()->two_factor_confirmed_at)]);
}

/**
* @throws DecorationException
* @throws PageException
*/

protected function twoFactorQrCodes(): MoonShineComponent
{
return Fragment::make([
FlexibleRender::make(static function () {
if (request('status') === 'qr' && is_null(auth()->user()->two_factor_confirmed_at)) {
return FormBuilder::make(route('moonshine-two-factor.confirm'))
->async(asyncEvents: [
'fragment-updated-qr-code',
'fragment-updated-recovery-code',
'fragment-updated-enable-disable',
return FormBuilder::make(route('moonshine.moonshine-two-factor.confirm'))
->async(events: [
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'qr-code'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'recovery-code'),
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'enable-disable'),
])
->fields(
array_filter([
Expand All @@ -131,13 +124,9 @@ protected function twoFactorQrCodes(): MoonShineComponent

return '';
}),
])->name('qr-code')->updateAsync(['status' => 'qr']);
])->name('qr-code')->updateWith(['status' => 'qr']);
}

/**
* @throws DecorationException
* @throws PageException
*/
protected function twoFactorRecoveryCodes(): MoonShineComponent
{
return Fragment::make([
Expand All @@ -156,17 +145,19 @@ protected function twoFactorRecoveryCodes(): MoonShineComponent
['button-clicked-refresh-codes'],
fn () => ActionButton::make(
__('moonshine-two-factor::ui.refresh_recovery_codes'),
route('moonshine-two-factor.refresh-codes')
route('moonshine.moonshine-two-factor.refresh-codes')
)
->customAttributes([
'style' => $this->twoFactorWithConfirm() ? 'display: none;' : '',
'x-on:button-clicked-refresh-codes.window' => 'request',
])
->async('POST', events: ['fragment-updated-recovery-code'])
->async(HttpMethod::POST, events: [
AlpineJs::event(JsEvent::FRAGMENT_UPDATED, 'recovery-code'),
])
),
]
),
])->name('recovery-code')->updateAsync();
])->name('recovery-code');
}

private function confirmAction(string $title, array $events, Closure $action): array
Expand All @@ -176,7 +167,7 @@ private function confirmAction(string $title, array $events, Closure $action): a
->inModal(
__('moonshine-two-factor::ui.confirm'),
FormBuilder::make(route('password.confirm'))
->async(asyncEvents: $events)
->async(events: $events)
->fields([
Password::make(trans('moonshine::ui.resource.password'), 'password')
->customAttributes(['autocomplete' => 'new-password'])
Expand Down
13 changes: 8 additions & 5 deletions src/Forms/ChallengeForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@

namespace MoonShine\TwoFactor\Forms;

use MoonShine\Components\FormBuilder;
use MoonShine\Fields\Password;
use MoonShine\Contracts\UI\FormBuilderContract;
use MoonShine\Contracts\UI\FormContract;
use MoonShine\UI\Components\FormBuilder;
use MoonShine\UI\Fields\Password;

final class ChallengeForm
final class ChallengeForm implements FormContract
{
public static function make(): FormBuilder
public function __invoke(): FormBuilderContract
{
return FormBuilder::make(route('moonshine-two-factor.check'))
return FormBuilder::make(route('moonshine.moonshine-two-factor.check'))
->class('authentication-form')
->fields([
Password::make(__('moonshine-two-factor::ui.code'), 'code')
->customAttributes(['autocomplete' => 'off'])
Expand Down
Loading

0 comments on commit 143756e

Please sign in to comment.