Updated to Laravel 12

This commit is contained in:
Will Browning 2025-07-29 14:59:24 +01:00
parent 322f19c300
commit 31b8798656
9 changed files with 64 additions and 71 deletions

View File

@ -1,41 +0,0 @@
<?php
namespace App\Actions;
use App\Facades\Webauthn;
use App\Models\WebauthnKey;
use Exception;
use Illuminate\Contracts\Auth\Authenticatable;
use LaravelWebauthn\Actions\RegisterKeyStore as ActionsRegisterKeyStore;
use LaravelWebauthn\Events\WebauthnRegister;
use LaravelWebauthn\Services\Webauthn\CredentialAttestationValidator;
use Webauthn\PublicKeyCredentialCreationOptions;
class RegisterKeyStore extends ActionsRegisterKeyStore
{
/**
* Register a new key.
*/
public function __invoke(Authenticatable $user, PublicKeyCredentialCreationOptions $publicKey, string $data, string $keyName): ?WebauthnKey
{
if (! Webauthn::canRegister($user)) {
$this->throwFailedRegisterException($user);
}
try {
$publicKeyCredentialSource = $this->app[CredentialAttestationValidator::class]($publicKey, $data);
$webauthnKey = Webauthn::create($user, $keyName, $publicKeyCredentialSource);
WebauthnRegister::dispatch($webauthnKey);
Webauthn::login();
return $webauthnKey;
} catch (Exception $e) {
$this->throwFailedRegisterException($user, $e);
}
return null;
}
}

View File

@ -3,8 +3,8 @@
namespace App\Facades; namespace App\Facades;
use Illuminate\Support\Facades\Facade; use Illuminate\Support\Facades\Facade;
use Webauthn\PublicKeyCredentialCreationOptions; use LaravelWebauthn\Services\Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialRequestOptions; use LaravelWebauthn\Services\Webauthn\PublicKeyCredentialRequestOptions;
/** /**
* @method static PublicKeyCredentialCreationOptions getRegisterData(\Illuminate\Contracts\Auth\Authenticatable $user) * @method static PublicKeyCredentialCreationOptions getRegisterData(\Illuminate\Contracts\Auth\Authenticatable $user)

View File

@ -8,17 +8,17 @@
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"require": { "require": {
"php": "^8.2", "php": "^8.2",
"asbiin/laravel-webauthn": "^4.0.0", "asbiin/laravel-webauthn": "5.0.1",
"bacon/bacon-qr-code": "^3.0", "bacon/bacon-qr-code": "^3.0",
"chillerlan/php-qrcode": "^5.0", "chillerlan/php-qrcode": "^5.0",
"guzzlehttp/guzzle": "^7.2", "guzzlehttp/guzzle": "^7.2",
"inertiajs/inertia-laravel": "^2.0", "inertiajs/inertia-laravel": "^2.0",
"laravel/framework": "^11.0", "laravel/framework": "^12.0",
"laravel/sanctum": "^4.0", "laravel/sanctum": "^4.0",
"laravel/tinker": "^2.7", "laravel/tinker": "^2.7",
"laravel/ui": "^4.0", "laravel/ui": "^4.0",
"maatwebsite/excel": "^3.1", "maatwebsite/excel": "^3.1",
"mews/captcha": "3.3.3", "mews/captcha": "^3.3.3",
"php-mime-mail-parser/php-mime-mail-parser": "^9.0", "php-mime-mail-parser/php-mime-mail-parser": "^9.0",
"pragmarx/google2fa-laravel": "^2.0.0", "pragmarx/google2fa-laravel": "^2.0.0",
"ramsey/uuid": "^4.0", "ramsey/uuid": "^4.0",

View File

@ -131,6 +131,20 @@ return [
'register' => 'vendor.webauthn.register', 'register' => 'vendor.webauthn.register',
], ],
/*
|--------------------------------------------------------------------------
| Webauthn logging
|--------------------------------------------------------------------------
|
| Here you may specify the channel to which Webauthn will log messages.
| This value should correspond with one of your loggers that is already
| present in your "logging" configuration file. If left as null, it will
| use the default logger for the application.
|
*/
'log' => 'null',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Session name | Session name
@ -266,6 +280,23 @@ return [
'user_verification' => 'discouraged', 'user_verification' => 'discouraged',
/*
|--------------------------------------------------------------------------
| The resident key
|--------------------------------------------------------------------------
|
| When userless is set to 'preferred' or 'required', the resident key will be
| forced to be 'required' automatically.
|
| See https://www.w3.org/TR/webauthn/#enum-residentKeyRequirement
|
| Supported: "null", "required", "preferred", "discouraged".
| Forced to "required" when userless is true.
|
*/
'resident_key' => null,
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Userless (One touch, Typeless) login | Userless (One touch, Typeless) login

View File

@ -217,13 +217,13 @@
<div <div
v-if="search" v-if="search"
@click=" @click="
;(searchForm.search = ''), ;((searchForm.search = ''),
$inertia.visit( $inertia.visit(
route(route().current(), omit(route().params, ['search', 'page'])), route(route().current(), omit(route().params, ['search', 'page'])),
{ {
only: ['initialRows', 'search'], only: ['initialRows', 'search'],
}, },
) ))
" "
class="absolute inset-y-0 right-0 cursor-pointer flex items-center pr-3 rounded-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" class="absolute inset-y-0 right-0 cursor-pointer flex items-center pr-3 rounded-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
> >

View File

@ -385,8 +385,8 @@
</span> </span>
<button <button
@click=" @click="
;(aliasIdToEdit = props.row.id), ;((aliasIdToEdit = props.row.id),
(aliasDescriptionToEdit = props.row.description) (aliasDescriptionToEdit = props.row.description))
" "
> >
<icon name="edit" class="inline-block w-6 h-6 text-grey-300 fill-current" /> <icon name="edit" class="inline-block w-6 h-6 text-grey-300 fill-current" />
@ -395,7 +395,7 @@
<div v-else> <div v-else>
<button <button
class="inline-block text-grey-300 text-sm py-1 border border-transparent" class="inline-block text-grey-300 text-sm py-1 border border-transparent"
@click=";(aliasIdToEdit = props.row.id), (aliasDescriptionToEdit = '')" @click=";((aliasIdToEdit = props.row.id), (aliasDescriptionToEdit = ''))"
> >
Add description Add description
</button> </button>
@ -695,10 +695,10 @@
It doesn't look like you have any aliases yet! It doesn't look like you have any aliases yet!
</h3> </h3>
<div v-if="subdomain"> <div v-if="subdomain">
<p class="mb-4 text-md text-grey-700">There {{ domain ? 'are two ways' : 'is one way'}} to create new aliases.</p> <p class="mb-4 text-md text-grey-700">
<h3 class="mb-2 text-lg text-indigo-800 font-semibold"> There {{ domain ? 'are two ways' : 'is one way' }} to create new aliases.
Create aliases on the fly </p>
</h3> <h3 class="mb-2 text-lg text-indigo-800 font-semibold">Create aliases on the fly</h3>
<p class="mb-2 text-grey-700"> <p class="mb-2 text-grey-700">
To create aliases on the fly all you have to do is make up any new alias and give that out To create aliases on the fly all you have to do is make up any new alias and give that out
instead of your real email address. instead of your real email address.
@ -720,19 +720,20 @@
</p> </p>
</div> </div>
<div v-if="domain"> <div v-if="domain">
<p v-if="!subdomain" class="mb-4 text-md text-grey-700">There is one way to create new aliases.</p> <p v-if="!subdomain" class="mb-4 text-md text-grey-700">
<h3 class="mb-2 text-lg text-indigo-800 font-semibold"> There is one way to create new aliases.
Create a unique random alias </p>
</h3> <h3 class="mb-2 text-lg text-indigo-800 font-semibold">Create a unique random alias</h3>
<p class="mb-2 text-grey-700"> <p class="mb-2 text-grey-700">
You can click the button above to create a random alias that will look something like this: You can click the button above to create a random alias that will look something like
this:
</p> </p>
<p class="mb-2 text-grey-700"> <p class="mb-2 text-grey-700">
<b>x481n904@{{ domain }}</b> <b>x481n904@{{ domain }}</b>
</p> </p>
<p clas="text-grey-700"> <p clas="text-grey-700">
This is useful if you do not wish to include your username in the email as a potential link This is useful if you do not wish to include your username in the email as a potential
between aliases. link between aliases.
</p> </p>
</div> </div>
<div class="mt-4"> <div class="mt-4">
@ -1312,8 +1313,8 @@
</p> </p>
<p class="mt-4 text-grey-700"> <p class="mt-4 text-grey-700">
<b>Shared Domain Aliases</b> - A shared domain alias is any alias that has a domain name <b>Shared Domain Aliases</b> - A shared domain alias is any alias that has a domain name
that is also shared with other users. Aliases with shared domain names must be pre-generated and cannot be that is also shared with other users. Aliases with shared domain names must be
created on-the-fly like standard aliases. pre-generated and cannot be created on-the-fly like standard aliases.
</p> </p>
<div class="mt-6 flex flex-col sm:flex-row"> <div class="mt-6 flex flex-col sm:flex-row">

View File

@ -105,14 +105,15 @@
}}</span> }}</span>
<button <button
@click=" @click="
;(domainIdToEdit = props.row.id), (domainDescriptionToEdit = props.row.description) ;((domainIdToEdit = props.row.id),
(domainDescriptionToEdit = props.row.description))
" "
> >
<icon name="edit" class="inline-block w-6 h-6 text-grey-300 fill-current" /> <icon name="edit" class="inline-block w-6 h-6 text-grey-300 fill-current" />
</button> </button>
</div> </div>
<div v-else class="flex justify-center"> <div v-else class="flex justify-center">
<button @click=";(domainIdToEdit = props.row.id), (domainDescriptionToEdit = '')"> <button @click=";((domainIdToEdit = props.row.id), (domainDescriptionToEdit = ''))">
<icon name="plus" class="block w-6 h-6 text-grey-300 fill-current" /> <icon name="plus" class="block w-6 h-6 text-grey-300 fill-current" />
</button> </button>
</div> </div>

View File

@ -190,8 +190,9 @@
</div> </div>
<div v-show="accessToken"> <div v-show="accessToken">
<p class="my-4 text-grey-700"> <p class="my-4 text-grey-700">
This is your new personal access key. <b>This is the only time the key will ever be This is your new personal access key.
displayed</b>, so please make a note of it in a safe place (e.g. password manager)! <b>This is the only time the key will ever be displayed</b>, so please make a note of it
in a safe place (e.g. password manager)!
</p> </p>
<textarea <textarea
v-model="accessToken" v-model="accessToken"

View File

@ -113,15 +113,15 @@
}}</span> }}</span>
<button <button
@click=" @click="
;(usernameIdToEdit = props.row.id), ;((usernameIdToEdit = props.row.id),
(usernameDescriptionToEdit = props.row.description) (usernameDescriptionToEdit = props.row.description))
" "
> >
<Icon name="edit" class="inline-block w-6 h-6 text-grey-300 fill-current" /> <Icon name="edit" class="inline-block w-6 h-6 text-grey-300 fill-current" />
</button> </button>
</div> </div>
<div v-else class="flex justify-center"> <div v-else class="flex justify-center">
<button @click=";(usernameIdToEdit = props.row.id), (usernameDescriptionToEdit = '')"> <button @click=";((usernameIdToEdit = props.row.id), (usernameDescriptionToEdit = ''))">
<Icon name="plus" class="block w-6 h-6 text-grey-300 fill-current" /> <Icon name="plus" class="block w-6 h-6 text-grey-300 fill-current" />
</button> </button>
</div> </div>