Admin can now hopefully add a passkey to their account
This commit is contained in:
parent
cadd58187a
commit
2fb8339d91
16 changed files with 351 additions and 40 deletions
93
app/Http/Controllers/Admin/PasskeysController.php
Normal file
93
app/Http/Controllers/Admin/PasskeysController.php
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Passkey;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\View\View;
|
||||
|
||||
/**
|
||||
* @psalm-suppress UnusedClass
|
||||
*/
|
||||
class PasskeysController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
$user = auth()->user();
|
||||
$passkeys = $user->passkey;
|
||||
|
||||
return view('admin.passkeys.index', compact('passkeys'));
|
||||
}
|
||||
|
||||
public function save(Request $request): JsonResponse
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'id' => 'required|string|unique:App\Models\Passkey,passkey_id',
|
||||
'public_key' => 'required|file',
|
||||
'transports' => 'required|json',
|
||||
'challenge' => 'required|string',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Passkey could not be saved (validation failed)',
|
||||
]);
|
||||
}
|
||||
|
||||
$validated = $validator->validated();
|
||||
|
||||
if (
|
||||
!session()->has('challenge') ||
|
||||
$validated['challenge'] !== session('challenge')
|
||||
) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Passkey could not be saved (challenge failed)',
|
||||
]);
|
||||
}
|
||||
|
||||
$passkey = new Passkey();
|
||||
$passkey->passkey_id = $validated['id'];
|
||||
$passkey->passkey = $validated['public_key']->get();
|
||||
$passkey->transports = json_decode($validated['transports'], true, 512, JSON_THROW_ON_ERROR);
|
||||
$passkey->user_id = auth()->user()->id;
|
||||
$passkey->save();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Passkey saved successfully',
|
||||
]);
|
||||
}
|
||||
|
||||
public function init(): JsonResponse
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$passkeys = $user->passkey()->get();
|
||||
|
||||
$existing = $passkeys->map(function (Passkey $passkey) {
|
||||
return [
|
||||
'id' => $passkey->passkey_id,
|
||||
'transports' => $passkey->transports,
|
||||
'type' => 'public-key',
|
||||
];
|
||||
})->all();
|
||||
|
||||
$challenge = Hash::make(random_bytes(32));
|
||||
session(['challenge' => $challenge]);
|
||||
|
||||
return response()->json([
|
||||
'challenge' => $challenge,
|
||||
'userId' => $user->name,
|
||||
'existing' => $existing,
|
||||
]);
|
||||
}
|
||||
}
|
42
app/Models/Passkey.php
Normal file
42
app/Models/Passkey.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Passkey extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* Save and access the passkey appropriately.
|
||||
*/
|
||||
protected function passkey(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: static fn ($value) => stream_get_contents($value),
|
||||
set: static fn ($value) => pg_escape_bytea($value),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save and access the transports appropriately.
|
||||
*/
|
||||
protected function transports(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: static fn ($value) => json_decode($value, true, 512, JSON_THROW_ON_ERROR),
|
||||
set: static fn ($value) => json_encode($value, JSON_THROW_ON_ERROR),
|
||||
);
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
|
@ -24,4 +25,9 @@ class User extends Authenticatable
|
|||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
public function passkey(): HasMany
|
||||
{
|
||||
return $this->hasMany(Passkey::class);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue