Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 1 addition & 43 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -60,30 +60,6 @@ parameters:
count: 1
path: src/Phaseolies/ApplicationBuilder.php

-
message: '#^Access to an undefined property Phaseolies\\Database\\Entity\\Model\:\:\$id\.$#'
identifier: property.notFound
count: 5
path: src/Phaseolies/Auth/Security/Authenticate.php

-
message: '#^Access to an undefined property Phaseolies\\Database\\Entity\\Model\:\:\$remember_token\.$#'
identifier: property.notFound
count: 2
path: src/Phaseolies/Auth/Security/Authenticate.php

-
message: '#^Access to an undefined property Phaseolies\\Database\\Entity\\Model\:\:\$two_factor_recovery_codes\.$#'
identifier: property.notFound
count: 4
path: src/Phaseolies/Auth/Security/Authenticate.php

-
message: '#^Access to an undefined property Phaseolies\\Database\\Entity\\Model\:\:\$two_factor_secret\.$#'
identifier: property.notFound
count: 3
path: src/Phaseolies/Auth/Security/Authenticate.php

-
message: '#^Access to undefined constant chillerlan\\QRCode\\QRCode\:\:ECC_M\.$#'
identifier: classConstant.notFound
Expand All @@ -97,7 +73,7 @@ parameters:
path: src/Phaseolies/Auth/Security/Authenticate.php

-
message: '#^Call to an undefined static method Phaseolies\\Database\\Entity\\Model\:\:where\(\)\.$#'
message: '#^Call to an undefined static method Phaseolies\\Auth\\Authable\:\:where\(\)\.$#'
identifier: staticMethod.notFound
count: 1
path: src/Phaseolies/Auth/Security/Authenticate.php
Expand Down Expand Up @@ -144,18 +120,6 @@ parameters:
count: 1
path: src/Phaseolies/Auth/Security/Authenticate.php

-
message: '#^Using nullsafe property access "\?\-\>id" on left side of \?\? is unnecessary\. Use \-\> instead\.$#'
identifier: nullsafe.neverNull
count: 1
path: src/Phaseolies/Auth/Security/Authenticate.php

-
message: '#^Using nullsafe property access on non\-nullable type Phaseolies\\Database\\Entity\\Model\. Use \-\> instead\.$#'
identifier: nullsafe.neverNull
count: 1
path: src/Phaseolies/Auth/Security/Authenticate.php

-
message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#'
identifier: function.alreadyNarrowedType
Expand Down Expand Up @@ -1656,12 +1620,6 @@ parameters:
count: 16
path: src/Phaseolies/Support/Collection.php

-
message: '#^Comparison operation "\<" between int\<80500, 80599\> and 70300 is always false\.$#'
identifier: smaller.alwaysFalse
count: 1
path: src/Phaseolies/Support/CookieJar.php

-
message: '#^PHPDoc tag @return with type void is incompatible with native type bool\.$#'
identifier: return.phpDocType
Expand Down
108 changes: 108 additions & 0 deletions src/Phaseolies/Auth/Authable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

namespace Phaseolies\Auth;

use Phaseolies\Database\Entity\Model;

/**
* @property int|string|null $id
* @property string|null $password
* @property string|null $remember_token
* @property string|null $two_factor_secret
* @property string|null $two_factor_recovery_codes
*/
abstract class Authable extends Model
{
/**
* Get the authentication key name used for identifying the user.
*
* @return string
*/
public function getAuthKeyName(): string
{
return 'email';
}

/**
* Get the unique identifier for the user.
*
* @return int|string|null
*/
public function getAuthIdentifier(): int|string|null
{
return $this->{$this->getKeyName()};
}

/**
* Get the password for the user.
*
* @return string|null
*/
public function getAuthPassword(): ?string
{
return $this->password;
}

/**
* Get the remember token value.
*
* @return string|null
*/
public function getRememberToken(): ?string
{
return $this->remember_token;
}

/**
* Set the remember token value.
*
* @param string|null $value
* @return void
*/
public function setRememberToken(?string $value): void
{
$this->remember_token = $value;
}

/**
* Get the two-factor authentication secret.
*
* @return string|null
*/
public function getTwoFactorSecret(): ?string
{
return $this->two_factor_secret;
}

/**
* Set the two-factor authentication secret.
*
* @param string|null $value
* @return void
*/
public function setTwoFactorSecret(?string $value): void
{
$this->two_factor_secret = $value;
}

/**
* Get the two-factor recovery codes.
*
* @return string|null
*/
public function getTwoFactorRecoveryCodes(): ?string
{
return $this->two_factor_recovery_codes;
}

/**
* Set the two-factor recovery codes.
*
* @param string|null $value
* @return void
*/
public function setTwoFactorRecoveryCodes(?string $value): void
{
$this->two_factor_recovery_codes = $value;
}
}
71 changes: 40 additions & 31 deletions src/Phaseolies/Auth/Security/Authenticate.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Phaseolies\Support\Facades\Hash;
use Phaseolies\Support\Facades\Crypt;
use Phaseolies\Database\Entity\Model;
use Phaseolies\Auth\Authable;

class Authenticate
{
Expand Down Expand Up @@ -32,16 +32,16 @@ class Authenticate
/**
* The current stateless user (for onceUsingId).
*
* @var Model|null
* @var Authable|null
*/
private $statelessUser = null;

/**
* Per-instance resolved user cache
*
* @var Model|null
* @var Authable|null
*/
private ?Model $resolvedUser = null;
private ?Authable $resolvedUser = null;

/**
* Create a new actor instance.
Expand Down Expand Up @@ -80,11 +80,20 @@ public function __get($name)
/**
* Resolve a fresh instance of the configured auth model.
*
* @return Model
* @return Authable
* @throws \InvalidArgumentException
*/
protected function getModel(): Model
protected function getModel(): Authable
{
return app($this->config['model']);
$model = app($this->config['model']);

if (!$model instanceof Authable) {
throw new \InvalidArgumentException(
'Auth model must extend ' . Authable::class . ', ' . get_debug_type($model) . ' given'
);
}

return $model;
}

/**
Expand Down Expand Up @@ -145,7 +154,7 @@ public function try(array $credentials = [], bool $remember = false): bool

$user = $authModel::where($customAuthKey, $authKeyValue)->first();

if (!$user || !Hash::check($password, $user->password)) {
if (!$user || !Hash::check($password, $user->getAuthPassword())) {
return false;
}

Expand All @@ -157,24 +166,24 @@ public function try(array $credentials = [], bool $remember = false): bool
/**
* Log in a user instance.
*
* @param Model $user
* @param Authable $user
* @param bool $remember
* @return bool
* @throws \InvalidArgumentException
*/
public function login($user, bool $remember = false): bool
public function login(Authable $user, bool $remember = false): bool
{
$authModel = $this->getModel();
$modelClass = $authModel::class;

if (!$user instanceof $modelClass) {
throw new \InvalidArgumentException(
"Argument #1 ($user) must be an instance of $modelClass " . gettype($user) . ' given'
'Argument #1 ($user) must be an instance of ' . $modelClass . ', ' . get_debug_type($user) . ' given'
);
}

if ($this->hasTwoFactorEnabled($user) && !$this->isApiRequest()) {
session()->put($this->getTwoFactorUserKey(), $user->id);
session()->put($this->getTwoFactorUserKey(), $user->getAuthIdentifier());
session()->put($this->getTwoFactorRememberKey(), $remember);

return true;
Expand All @@ -194,9 +203,9 @@ public function login($user, bool $remember = false): bool
*
* @param int $id
* @param bool $remember
* @return Model|null
* @return Authable|null
*/
public function loginUsingId(int $id, bool $remember = false): ?Model
public function loginUsingId(int $id, bool $remember = false): ?Authable
{
$authModel = $this->getModel();

Expand All @@ -213,9 +222,9 @@ public function loginUsingId(int $id, bool $remember = false): ?Model
* Log in a user by their ID for a single request (no session/cookie).
*
* @param int $id
* @return Model|null
* @return Authable|null
*/
public function onceUsingId(int $id): ?Model
public function onceUsingId(int $id): ?Authable
{
$authModel = $this->getModel();

Expand All @@ -233,9 +242,9 @@ public function onceUsingId(int $id): ?Model
/**
* Get the currently authenticated user.
*
* @return Model|null
* @return Authable|null
*/
public function user(): ?Model
public function user(): ?Authable
{
if ($this->resolvedUser !== null) {
return $this->resolvedUser;
Expand Down Expand Up @@ -299,14 +308,14 @@ public function user(): ?Model

$user = $authModel::find($id);

if (!$user || !$user->remember_token) {
if (!$user || !$user->getRememberToken()) {
$this->expireRememberCookie();
return null;
}

if (Hash::check($token, $user->remember_token)) {
if (Hash::check($token, $user->getRememberToken())) {
if ($this->hasTwoFactorEnabled($user)) {
session()->put($this->getTwoFactorUserKey(), $user->id);
session()->put($this->getTwoFactorUserKey(), $user->getAuthIdentifier());
session()->put($this->getTwoFactorRememberKey(), true);
}

Expand All @@ -317,7 +326,7 @@ public function user(): ?Model

// Token didn't match - possible theft attempt
$this->expireRememberCookie();
$user->remember_token = null;
$user->setRememberToken(null);
$user->save();
}

Expand All @@ -343,9 +352,9 @@ public function logout(): void
{
$user = $this->user();

if ($user && $user?->remember_token) {
$user->remember_token = null;
$user->withoutHook();
if ($user && $user->getRememberToken()) {
$user->setRememberToken(null);
$user::withoutHook();
$user->save();
}

Expand All @@ -370,13 +379,13 @@ public function logout(): void
/**
* Set the authenticated user in the session.
*
* @param Model $user
* @param Authable $user
*/
private function setUser(Model $user): void
private function setUser(Authable $user): void
{
session()->regenerate();

session()->put($this->getSessionKey(), $user->id);
session()->put($this->getSessionKey(), $user->getAuthIdentifier());

$this->resolvedUser = $user;
}
Expand All @@ -395,11 +404,11 @@ public function viaRemember(): bool
/**
* Get the authenticated user id
*
* @return int|null
* @return int|string|null
*/
public function id(): ?int
public function id(): int|string|null
{
return $this->user()?->id ?? null;
return $this->user()?->getAuthIdentifier();
}

/**
Expand Down
Loading
Loading