diff --git a/.gitattributes b/.gitattributes index 4b62b65..220b608 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,4 +10,5 @@ /tests export-ignore /.editorconfig export-ignore /.styleci.yml export-ignore -/composer.lock export-ignore \ No newline at end of file +/composer.lock export-ignore +/phpstan.neon export-ignore diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 51f4425..9e57d13 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -53,6 +53,7 @@ jobs: - 8.1 - 8.2 - 8.3 + - 8.4 laravel-constraint: - 10.* - 11.* diff --git a/composer.json b/composer.json index 6925660..2596a41 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,8 @@ }, "require-dev": { "ext-sodium": "*", - "orchestra/testbench": "8.*|9.*" + "orchestra/testbench": "8.*|9.*", + "livewire/livewire": "3.*" }, "suggest": { "paragonie/sodium_compat": "To enable EdDSA 25519 keys from authenticators, if `ext-sodium` is unavailable." diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..b533974 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,3 @@ +parameters: + ignoreErrors: + - '#Trait Laragear\\WebAuthn\\WebAuthnAuthentication is used zero times and is not analysed\.#' diff --git a/resources/views/livewire/attest.blade.php b/resources/views/livewire/attest.blade.php new file mode 100644 index 0000000..e69de29 diff --git a/src/Assertion/Validator/AssertionValidation.php b/src/Assertion/Validator/AssertionValidation.php index 7db933c..7947fa9 100644 --- a/src/Assertion/Validator/AssertionValidation.php +++ b/src/Assertion/Validator/AssertionValidation.php @@ -38,7 +38,7 @@ public function __construct( /** * Create a new Assertion Validation instance from a WebAuthn request data. */ - public static function fromRequest(Request $request = null): static + public static function fromRequest(?Request $request = null): static { // @phpstan-ignore-next-line return new static(new JsonTransport(($request ?? app('request'))->only(static::REQUEST_KEYS))); diff --git a/src/Assertion/Validator/Pipes/CheckPublicKeySignature.php b/src/Assertion/Validator/Pipes/CheckPublicKeySignature.php index 30d1e8f..9ab7ea6 100644 --- a/src/Assertion/Validator/Pipes/CheckPublicKeySignature.php +++ b/src/Assertion/Validator/Pipes/CheckPublicKeySignature.php @@ -65,7 +65,7 @@ protected function retrieveBinaryVerifiable(JsonTransport $request): string $verifiable = ByteBuffer::decodeBase64Url($request->get('response.authenticatorData')). hash('sha256', ByteBuffer::decodeBase64Url($request->get('response.clientDataJSON')), true); - return $verifiable + return $verifiable // @phpstan-ignore-line ?: throw AssertionException::make('Authenticator Data or Client Data JSON are empty or malformed.'); } diff --git a/src/Attestation/AuthenticatorData.php b/src/Attestation/AuthenticatorData.php index dd06c7b..301e8f9 100644 --- a/src/Attestation/AuthenticatorData.php +++ b/src/Attestation/AuthenticatorData.php @@ -381,7 +381,7 @@ protected static function readAttestData(string $binary, int &$endOffset): objec $length = unpack('nlength', substr($binary, 53, 2))['length']; // Set end offset. - $endOffset = 55 + $length; + $endOffset = 55 + $length; // @phpstan-ignore-line return (object) [ 'aaguid' => substr($binary, 37, 16), @@ -395,7 +395,7 @@ protected static function readAttestData(string $binary, int &$endOffset): objec */ protected static function readCredentialPublicKey(string $binary, int $offset, int &$endOffset): object { - $enc = CborDecoder::decodePortion($binary, $offset, $endOffset); + $enc = CborDecoder::decodePortion($binary, $offset, $endOffset); // @phpstan-ignore-line // COSE key-encoded elliptic curve public key in EC2 format $publicKey = (object) [ diff --git a/src/Attestation/Validator/AttestationValidation.php b/src/Attestation/Validator/AttestationValidation.php index 5ed4466..4d3d3fb 100644 --- a/src/Attestation/Validator/AttestationValidation.php +++ b/src/Attestation/Validator/AttestationValidation.php @@ -38,7 +38,7 @@ public function __construct( /** * Create a new Attestation Creation instance from a request and a user. */ - public static function fromRequest(Request $request = null, WebAuthnAuthenticatable $user = null): static + public static function fromRequest(?Request $request = null, ?WebAuthnAuthenticatable $user = null): static { // @phpstan-ignore-next-line return new static($user, new JsonTransport(($request ?? app('request'))->only(static::REQUEST_KEYS))); diff --git a/src/Auth/WebAuthnUserProvider.php b/src/Auth/WebAuthnUserProvider.php index 77c5090..74fd47f 100644 --- a/src/Auth/WebAuthnUserProvider.php +++ b/src/Auth/WebAuthnUserProvider.php @@ -123,6 +123,7 @@ protected function validateWebAuthn(WebAuthnAuthenticatable $user, array $creden */ public function rehashPasswordIfRequired(UserContract $user, array $credentials, bool $force = false): void { + // @phpstan-ignore-next-line if (! $this->isSignedChallenge($credentials) && method_exists(get_parent_class($this), 'rehashPasswordIfRequired')) { parent::rehashPasswordIfRequired($user, $credentials, $force); } diff --git a/src/ByteBuffer.php b/src/ByteBuffer.php index bd63ef1..a72325e 100644 --- a/src/ByteBuffer.php +++ b/src/ByteBuffer.php @@ -144,7 +144,7 @@ public function hashNotEqual(self|string $buffer): bool /** * Returns a certain portion of these bytes. */ - public function getBytes(int $offset = 0, int $length = null): string + public function getBytes(int $offset = 0, ?int $length = null): string { $length ??= $this->dataLength; diff --git a/src/CborDecoder.php b/src/CborDecoder.php index bde0930..1c9db77 100644 --- a/src/CborDecoder.php +++ b/src/CborDecoder.php @@ -103,7 +103,7 @@ public static function decode(ByteBuffer|string $encoded): ByteBuffer|array|bool * * @throws \Laragear\WebAuthn\Exceptions\DataException */ - public static function decodePortion(ByteBuffer|string $bufOrBin, int $startOffset, ?int &$endOffset = null): ByteBuffer|array|bool|float|int|string|null + public static function decodePortion(ByteBuffer|string $bufOrBin, int $startOffset, ?int &$endOffset = null): ByteBuffer|array|bool|float|int|string|null // @phpstan-ignore-line { $buf = $bufOrBin instanceof ByteBuffer ? $bufOrBin : new ByteBuffer($bufOrBin); diff --git a/src/Contracts/WebAuthnAuthenticatable.php b/src/Contracts/WebAuthnAuthenticatable.php index c7895dc..513752f 100644 --- a/src/Contracts/WebAuthnAuthenticatable.php +++ b/src/Contracts/WebAuthnAuthenticatable.php @@ -39,10 +39,6 @@ public function makeWebAuthnCredential(array $properties): WebAuthnCredential; /** * Returns a queryable relationship for its WebAuthn Credentials. - * - * @phpstan-ignore-next-line - * - * @return \Illuminate\Database\Eloquent\Relations\MorphMany|\Laragear\WebAuthn\Models\WebAuthnCredential */ public function webAuthnCredentials(): MorphMany; } diff --git a/src/Http/Requests/AssertedRequest.php b/src/Http/Requests/AssertedRequest.php index caf2b02..93136b9 100644 --- a/src/Http/Requests/AssertedRequest.php +++ b/src/Http/Requests/AssertedRequest.php @@ -42,18 +42,12 @@ public function hasRemember(): bool /** * Logs in the user for this assertion request. - * - * @param string|null $guard - * - * @phpstan-ignore-next-line - * - * @return \Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable|\Illuminate\Contracts\Auth\Authenticatable|null */ public function login( - string $guard = null, - bool $remember = null, + ?string $guard = null, + ?bool $remember = null, bool $destroySession = false, - callable|array $callbacks = null + callable|array|null $callbacks = null ): ?WebAuthnAuthenticatable { /** @var \Illuminate\Contracts\Auth\StatefulGuard $auth */ $auth = auth()->guard($guard); diff --git a/src/JsonTransport.php b/src/JsonTransport.php index b0fd0f5..b5e9b9f 100644 --- a/src/JsonTransport.php +++ b/src/JsonTransport.php @@ -38,7 +38,7 @@ public function set(string $key, mixed $value): void /** * Retrieves a value from the underlying JSON array. */ - public function get(string $key, string|int $default = null): mixed + public function get(string $key, string|int|null $default = null): mixed { return Arr::get($this->json, $key, $default); } diff --git a/src/Models/WebAuthnCredential.php b/src/Models/WebAuthnCredential.php index 956de57..a5ef88c 100644 --- a/src/Models/WebAuthnCredential.php +++ b/src/Models/WebAuthnCredential.php @@ -17,8 +17,8 @@ /** * @mixin \Illuminate\Database\Eloquent\Builder * - * @method \Illuminate\Database\Eloquent\Builder|\static newQuery() - * @method static \Illuminate\Database\Eloquent\Builder|\static query() + * @method \Illuminate\Database\Eloquent\Builder|static newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|static query() * @method static \Laragear\WebAuthn\Models\WebAuthnCredential make(array $attributes = []) * @method static \Laragear\WebAuthn\Models\WebAuthnCredential create(array $attributes = []) * @method static \Laragear\WebAuthn\Models\WebAuthnCredential forceCreate(array $attributes) @@ -27,20 +27,20 @@ * @method \Laragear\WebAuthn\Models\WebAuthnCredential firstOrNew(array $attributes = [], array $values = []) * @method \Laragear\WebAuthn\Models\WebAuthnCredential firstOrFail($columns = ['*']) * @method \Laragear\WebAuthn\Models\WebAuthnCredential firstOrCreate(array $attributes, array $values = []) - * @method \Laragear\WebAuthn\Models\WebAuthnCredential firstOr($columns = ['*'], \Closure $callback = null) + * @method \Laragear\WebAuthn\Models\WebAuthnCredential firstOr($columns = ['*'], \Closure|null $callback = null) * @method \Laragear\WebAuthn\Models\WebAuthnCredential firstWhere($column, $operator = null, $value = null, $boolean = 'and') * @method \Laragear\WebAuthn\Models\WebAuthnCredential updateOrCreate(array $attributes, array $values = []) * @method \Laragear\WebAuthn\Models\WebAuthnCredential createOrFirst(array $attributes, array $values = []) * @method \Laragear\WebAuthn\Models\WebAuthnCredential sole($columns = ['*']) * @method \Laragear\WebAuthn\Models\WebAuthnCredential findOrNew($id, $columns = ['*']) - * @method \Illuminate\Database\Eloquent\Collection|\static[]|\static|null find($id, $columns = ['*']) - * @method \Illuminate\Database\Eloquent\Collection|\static[]|\static findOrFail($id, $columns = ['*']) - * @method \Illuminate\Database\Eloquent\Collection|\static[]|\static findOr($id, $columns = ['*'], \Closure $callback = null) - * @method \Illuminate\Database\Eloquent\Collection|\static[] findMany($id, $columns = ['*']) - * @method \Illuminate\Database\Eloquent\Collection|\static[] fromQuery($query, $bindings = []) - * @method \Illuminate\Support\LazyCollection|\static[] lazy(int $chunkSize = 1000) - * @method \Illuminate\Support\LazyCollection|\static[] lazyById(int $chunkSize = 1000, string|null $column = null, string|null $alias = null) - * @method \Illuminate\Support\LazyCollection|\static[] lazyByIdDesc(int $chunkSize = 1000, string|null $column = null, string|null $alias = null) + * @method \Illuminate\Database\Eloquent\Collection|static[]|static|null find($id, $columns = ['*']) + * @method \Illuminate\Database\Eloquent\Collection|static[]|static findOrFail($id, $columns = ['*']) + * @method \Illuminate\Database\Eloquent\Collection|static[]|static findOr($id, $columns = ['*'], \Closure|null $callback = null) + * @method \Illuminate\Database\Eloquent\Collection|static[] findMany($id, $columns = ['*']) + * @method \Illuminate\Database\Eloquent\Collection|static[] fromQuery($query, $bindings = []) + * @method \Illuminate\Support\LazyCollection|static[] lazy(int $chunkSize = 1000) + * @method \Illuminate\Support\LazyCollection|static[] lazyById(int $chunkSize = 1000, string|null $column = null, string|null $alias = null) + * @method \Illuminate\Support\LazyCollection|static[] lazyByIdDesc(int $chunkSize = 1000, string|null $column = null, string|null $alias = null) * * @property-read string $id * @property-read string $user_id @@ -59,8 +59,8 @@ * @property-read \Illuminate\Support\Carbon $created_at * @property-read \Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable $authenticatable * - * @method \Illuminate\Database\Eloquent\Builder|\static whereEnabled() - * @method \Illuminate\Database\Eloquent\Builder|\static whereDisabled() + * @method \Illuminate\Database\Eloquent\Builder|static whereEnabled() + * @method \Illuminate\Database\Eloquent\Builder|static whereDisabled() */ class WebAuthnCredential extends Model { @@ -101,13 +101,11 @@ class WebAuthnCredential extends Model protected $visible = ['id', 'origin', 'alias', 'aaguid', 'attestation_format', 'disabled_at']; /** - * @phpstan-ignore-next-line - * - * @return \Illuminate\Database\Eloquent\Relations\MorphTo|\Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable + * @return \Illuminate\Database\Eloquent\Relations\MorphTo<\Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable, $this> */ - public function authenticatable(): MorphTo + public function authenticatable(): MorphTo // @phpstan-ignore-line { - return $this->morphTo('authenticatable'); + return $this->morphTo('authenticatable'); // @phpstan-ignore-line } /** diff --git a/src/WebAuthn.php b/src/WebAuthn.php index 77561ae..0be083c 100644 --- a/src/WebAuthn.php +++ b/src/WebAuthn.php @@ -9,4 +9,13 @@ class WebAuthn extends Http\Routes { // + + public function something() + { + $some = ['foo', 'bar']; + + foreach ($some as $key => $value) { + // code... + } + } } diff --git a/src/WebAuthnServiceProvider.php b/src/WebAuthnServiceProvider.php index 78885a6..141e755 100644 --- a/src/WebAuthnServiceProvider.php +++ b/src/WebAuthnServiceProvider.php @@ -70,7 +70,7 @@ public function boot(): void */ protected function publishesPackageMigrations(array|string $paths, string $groups = 'migrations'): void { - if (method_exists(static::class, 'publishesMigrations')) { + if (method_exists(static::class, 'publishesMigrations')) { // @phpstan-ignore-line foreach ((array) $paths as $path) { $this->publishesMigrations([$path => $this->app->databasePath('migrations/')], 'migrations'); } @@ -88,7 +88,7 @@ protected function publishesPackageMigrations(array|string $paths, string $group $files[$file->getRealPath()] = $this->app->databasePath("migrations/{$prefix}_$filename"); } - method_exists($this, 'publishesMigrations') + method_exists($this, 'publishesMigrations') // @phpstan-ignore-line ? $this->publishesMigrations($files, $groups) : $this->publishes($files, $groups); }