First release
This commit is contained in:
65
src/Http/Requests/AssertedRequest.php
Normal file
65
src/Http/Requests/AssertedRequest.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Laragear\WebAuthn\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable;
|
||||
|
||||
class AssertedRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
#[ArrayShape([
|
||||
'id' => "string", 'rawId' => "string", 'response.authenticatorData' => "string",
|
||||
'response.clientDataJSON' => "string", 'response.signature' => "string", 'response.userHandle' => "string",
|
||||
'type' => "string"
|
||||
])]
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'id' => 'required|string',
|
||||
'rawId' => 'required|string',
|
||||
'response.authenticatorData' => 'required|string',
|
||||
'response.clientDataJSON' => 'required|string',
|
||||
'response.signature' => 'required|string',
|
||||
'response.userHandle' => 'sometimes|nullable',
|
||||
'type' => 'required|string',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the login request wants to remember the user as stateful.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRemember(): bool
|
||||
{
|
||||
return $this->hasHeader('X-WebAuthn-Remember')
|
||||
|| $this->hasHeader('WebAuthn-Remember')
|
||||
|| $this->filled('remember');
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs in the user for this assertion request.
|
||||
*
|
||||
* @param string|null $guard
|
||||
* @return \Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable&\Illuminate\Contracts\Auth\Authenticatable|null
|
||||
*/
|
||||
public function login(string $guard = null, bool $remember = null, bool $destroySession = false): ?WebAuthnAuthenticatable
|
||||
{
|
||||
$auth = Auth::guard($guard);
|
||||
|
||||
if ($auth->attempt($this->validated(), $remember ?? $this->hasRemember())) {
|
||||
$this->session()->regenerate($destroySession);
|
||||
|
||||
return $auth->user();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
149
src/Http/Requests/AssertionRequest.php
Normal file
149
src/Http/Requests/AssertionRequest.php
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace Laragear\WebAuthn\Http\Requests;
|
||||
|
||||
use Illuminate\Contracts\Support\Responsable;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use InvalidArgumentException;
|
||||
use Laragear\WebAuthn\Assertion\Creator\AssertionCreation;
|
||||
use Laragear\WebAuthn\Assertion\Creator\AssertionCreator;
|
||||
use Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable;
|
||||
use Laragear\WebAuthn\WebAuthn;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
|
||||
class AssertionRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* The Assertion Creation instance.
|
||||
*
|
||||
* @var \Laragear\WebAuthn\Assertion\Creator\AssertionCreation
|
||||
*/
|
||||
protected AssertionCreation $assertion;
|
||||
|
||||
/**
|
||||
* The guard to use to retrieve the user.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected ?string $guard = null;
|
||||
|
||||
/**
|
||||
* If the user may or may not be verified on login.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected ?string $userVerification = null;
|
||||
|
||||
/**
|
||||
* Validate the class instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function validateResolved(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Return or make a new Assertion Creation.
|
||||
*
|
||||
* @return \Laragear\WebAuthn\Assertion\Creator\AssertionCreation
|
||||
*/
|
||||
protected function assertion(): AssertionCreation
|
||||
{
|
||||
return $this->assertion ??= new AssertionCreation($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the WebAuthn-compatible guard to use.
|
||||
*
|
||||
* @param string $guard
|
||||
* @return $this
|
||||
*/
|
||||
public function guard(string $guard): static
|
||||
{
|
||||
$this->guard = $guard;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the authenticator to only check for user presence on login.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fastLogin(): static
|
||||
{
|
||||
$this->assertion()->userVerification = WebAuthn::USER_VERIFICATION_DISCOURAGED;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the authenticator to always verify the user thoroughly on login.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function secureLogin(): static
|
||||
{
|
||||
$this->assertion()->userVerification = WebAuthn::USER_VERIFICATION_REQUIRED;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an assertion challenge for a user if found.
|
||||
*
|
||||
* @param \Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable|string|int|array|null $credentials
|
||||
* @return \Illuminate\Contracts\Support\Responsable
|
||||
* @throws \Illuminate\Contracts\Container\BindingResolutionException
|
||||
*/
|
||||
public function toVerify(WebAuthnAuthenticatable|string|int|array|null $credentials = []): Responsable
|
||||
{
|
||||
$this->assertion()->user = $this->findUser($credentials);
|
||||
|
||||
return $this->container
|
||||
->make(AssertionCreator::class)
|
||||
->send($this->assertion)
|
||||
->then(static function (AssertionCreation $creation): Responsable {
|
||||
return $creation->json;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find a user to create an assertion procedure.
|
||||
*
|
||||
* @param \Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable|array|int|string|null $credentials
|
||||
* @return \Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable|null
|
||||
* @throws \Illuminate\Contracts\Container\BindingResolutionException
|
||||
*/
|
||||
protected function findUser(WebAuthnAuthenticatable|array|int|string|null $credentials): ?WebAuthnAuthenticatable
|
||||
{
|
||||
if (!$credentials) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($credentials instanceof WebAuthnAuthenticatable) {
|
||||
return $credentials;
|
||||
}
|
||||
|
||||
// If the developer is using a string or integer, we will understand its trying to
|
||||
// retrieve by its ID, otherwise we will fall back to credentials. Once done, we
|
||||
// will check it uses WebAuthn if is not null, otherwise we'll fail miserably.
|
||||
$user = is_string($credentials) || is_int($credentials)
|
||||
? Auth::guard($this->guard)->getProvider()->retrieveById($credentials)
|
||||
: Auth::guard($this->guard)->getProvider()->retrieveByCredentials($credentials);
|
||||
|
||||
if ($user && ! $user instanceof WebAuthnAuthenticatable) {
|
||||
$guard = $this->guard ?? $this->container->make('config')->get('auth.defaults.guard');
|
||||
|
||||
throw new InvalidArgumentException(
|
||||
"The user found for the [$guard] auth guard is not an instance of [WebAuthnAuthenticatable]."
|
||||
);
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
120
src/Http/Requests/AttestationRequest.php
Normal file
120
src/Http/Requests/AttestationRequest.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace Laragear\WebAuthn\Http\Requests;
|
||||
|
||||
use Illuminate\Contracts\Support\Responsable;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Laragear\WebAuthn\Attestation\Creator\AttestationCreation;
|
||||
use Laragear\WebAuthn\Attestation\Creator\AttestationCreator;
|
||||
use Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable;
|
||||
use Laragear\WebAuthn\WebAuthn;
|
||||
|
||||
/**
|
||||
* @method \Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable user($guard = null)
|
||||
*/
|
||||
class AttestationRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* The attestation instance that would be returned.
|
||||
*
|
||||
* @var \Laragear\WebAuthn\Attestation\Creator\AttestationCreation
|
||||
*/
|
||||
protected AttestationCreation $attestation;
|
||||
|
||||
/**
|
||||
* Validate the class instance.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function validateResolved(): void
|
||||
{
|
||||
if (!$this->passesAuthorization()) {
|
||||
$this->failedAuthorization();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @param \Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable|null $user
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(?WebAuthnAuthenticatable $user): bool
|
||||
{
|
||||
return (bool) $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the existing attestation instance.
|
||||
*
|
||||
* @return \Laragear\WebAuthn\Attestation\Creator\AttestationCreation
|
||||
*/
|
||||
protected function attestation(): AttestationCreation
|
||||
{
|
||||
return $this->attestation ??= new AttestationCreation($this->user(), $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the authenticator to only check for user presence on registration.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fastRegistration(): static
|
||||
{
|
||||
$this->attestation()->userVerification = WebAuthn::USER_VERIFICATION_DISCOURAGED;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the authenticator to always verify the user thoroughly on registration.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function secureRegistration(): static
|
||||
{
|
||||
$this->attestation()->userVerification = WebAuthn::USER_VERIFICATION_REQUIRED;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the authenticator use this credential to login instantly, instead of asking for one.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function userless(): static
|
||||
{
|
||||
$this->attestation()->residentKey = WebAuthn::RESIDENT_KEY_REQUIRED;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the device to create multiple credentials for the same user for this app.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function allowDuplicates(): static
|
||||
{
|
||||
$this->attestation()->uniqueCredentials = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a response with the instructions to create a WebAuthn Credential.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Support\Responsable
|
||||
*/
|
||||
public function toCreate(): Responsable
|
||||
{
|
||||
return $this->container
|
||||
->make(AttestationCreator::class)
|
||||
->send($this->attestation())
|
||||
->then(static function (AttestationCreation $creation): Responsable {
|
||||
return $creation->json;
|
||||
});
|
||||
}
|
||||
}
|
||||
89
src/Http/Requests/AttestedRequest.php
Normal file
89
src/Http/Requests/AttestedRequest.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Laragear\WebAuthn\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use Laragear\WebAuthn\Attestation\Validator\AttestationValidation;
|
||||
use Laragear\WebAuthn\Attestation\Validator\AttestationValidator;
|
||||
use Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable;
|
||||
use Laragear\WebAuthn\Events\CredentialCreated;
|
||||
use Laragear\WebAuthn\Models\WebAuthnCredential;
|
||||
use function is_callable;
|
||||
|
||||
/**
|
||||
* @method \Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable user($guard = null)
|
||||
*/
|
||||
class AttestedRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* The new credential instance.
|
||||
*
|
||||
* @var \Laragear\WebAuthn\Models\WebAuthnCredential
|
||||
*/
|
||||
protected WebAuthnCredential $credential;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @param \Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable|null $user
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(?WebAuthnAuthenticatable $user): bool
|
||||
{
|
||||
return (bool) $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
#[ArrayShape([
|
||||
'id' => "string", 'rawId' => "string", 'response' => "string", 'response.clientDataJSON' => "string",
|
||||
'response.attestationObject' => "string", 'type' => "string"
|
||||
])]
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'id' => 'required|string',
|
||||
'rawId' => 'required|string',
|
||||
'response' => 'required|array',
|
||||
'response.clientDataJSON' => 'required|string',
|
||||
'response.attestationObject' => 'required|string',
|
||||
'type' => 'required|string',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a passed validation attempt.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Illuminate\Contracts\Container\BindingResolutionException
|
||||
*/
|
||||
protected function passedValidation(): void
|
||||
{
|
||||
$this->credential = $this->container->make(AttestationValidator::class)
|
||||
->send(new AttestationValidation($this->user(), $this))
|
||||
->then(static function (AttestationValidation $validation): WebAuthnCredential {
|
||||
return $validation->credential;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save and return the generated WebAuthn Credentials.
|
||||
*
|
||||
* @param array|callable $saving
|
||||
* @return string
|
||||
*/
|
||||
public function save(array|callable $saving = []): string
|
||||
{
|
||||
is_callable($saving) ? $saving($this->credential) : $this->credential->forceFill($saving);
|
||||
|
||||
$this->credential->save();
|
||||
|
||||
CredentialCreated::dispatch($this->user(), $this->credential);
|
||||
|
||||
return $this->credential->getKey();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user