add auth sessions

This commit is contained in:
Yisroel Baum 2026-07-31 09:51:39 +03:00
parent a37c8da555
commit 34e26f81f5
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
9 changed files with 243 additions and 0 deletions

View file

@ -0,0 +1,10 @@
<?php
namespace App\Auth;
use DateTimeImmutable;
interface Clock
{
public function now(): DateTimeImmutable;
}

View file

@ -0,0 +1,16 @@
<?php
namespace App\Auth;
use App\User\User;
use DateTimeImmutable;
final readonly class CreateSessionDto
{
public function __construct(
public string $token,
public User $user,
public DateTimeImmutable $createdAt,
public DateTimeImmutable $expiresAt,
) {}
}

View file

@ -0,0 +1,63 @@
<?php
namespace App\Auth;
use App\User\UserRepository;
use Carbon\CarbonInterface;
use DateTimeImmutable;
use DateTimeZone;
class EloquentSessionRepository implements SessionRepository
{
public function __construct(
private UserRepository $userRepository,
) {}
public function create(CreateSessionDto $dto): Session
{
SessionModel::create([
'token' => $dto->token,
'user_id' => $dto->user->getId(),
'created_at' => $dto->createdAt,
'expires_at' => $dto->expiresAt,
]);
return new Session(
token: $dto->token,
user: $dto->user,
createdAt: $dto->createdAt,
expiresAt: $dto->expiresAt,
);
}
public function findByToken(string $token): ?Session
{
$model = SessionModel::find($token);
if ($model === null) {
return null;
}
$user = $this->userRepository->find($model->user_id);
if ($user === null) {
return null;
}
return new Session(
token: $model->token,
user: $user,
createdAt: $this->toUtc($model->created_at),
expiresAt: $this->toUtc($model->expires_at),
);
}
public function deleteByToken(string $token): void
{
SessionModel::where('token', $token)->delete();
}
private function toUtc(CarbonInterface $dateTime): DateTimeImmutable
{
return DateTimeImmutable::createFromInterface($dateTime)
->setTimezone(new DateTimeZone('UTC'));
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace App\Auth;
use App\User\User;
use DateTimeImmutable;
final readonly class Session
{
public function __construct(
private string $token,
private User $user,
private DateTimeImmutable $createdAt,
private DateTimeImmutable $expiresAt,
) {}
public function getToken(): string
{
return $this->token;
}
public function getUser(): User
{
return $this->user;
}
public function getCreatedAt(): DateTimeImmutable
{
return $this->createdAt;
}
public function getExpiresAt(): DateTimeImmutable
{
return $this->expiresAt;
}
public function isExpired(DateTimeImmutable $now): bool
{
return $now >= $this->expiresAt;
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace App\Auth;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
/**
* @property string $token
* @property int $user_id
* @property CarbonInterface $created_at
* @property CarbonInterface $expires_at
*
* @method static Builder<static>|SessionModel newModelQuery()
* @method static Builder<static>|SessionModel newQuery()
* @method static Builder<static>|SessionModel query()
*
* @mixin \Eloquent
*/
#[Fillable([
'token',
'user_id',
'created_at',
'expires_at',
])]
class SessionModel extends Model
{
protected $table = 'auth_sessions';
protected $primaryKey = 'token';
public $incrementing = false;
protected $keyType = 'string';
public $timestamps = false;
/**
* @return array<string, string>
*/
protected function casts(): array
{
return [
'created_at' => 'datetime',
'expires_at' => 'datetime',
];
}
}

View file

@ -0,0 +1,12 @@
<?php
namespace App\Auth;
interface SessionRepository
{
public function create(CreateSessionDto $dto): Session;
public function findByToken(string $token): ?Session;
public function deleteByToken(string $token): void;
}

View file

@ -0,0 +1,17 @@
<?php
namespace App\Auth;
use DateTimeImmutable;
use DateTimeZone;
class SystemClock implements Clock
{
public function now(): DateTimeImmutable
{
return new DateTimeImmutable(
'now',
new DateTimeZone('UTC'),
);
}
}

View file

@ -2,6 +2,10 @@
namespace App\Providers;
use App\Auth\Clock;
use App\Auth\EloquentSessionRepository;
use App\Auth\SessionRepository;
use App\Auth\SystemClock;
use App\User\EloquentUserRepository;
use App\User\UserRepository;
use Carbon\CarbonImmutable;
@ -21,6 +25,11 @@ class AppServiceProvider extends ServiceProvider
UserRepository::class,
EloquentUserRepository::class,
);
$this->app->bind(
SessionRepository::class,
EloquentSessionRepository::class,
);
$this->app->bind(Clock::class, SystemClock::class);
}
/**

View file

@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('auth_sessions', function (Blueprint $table): void {
$table->string('token', 64)->primary();
$table->foreignId('user_id')
->constrained('users')
->cascadeOnDelete();
$table->timestamp('created_at');
$table->timestamp('expires_at')->index();
});
}
public function down(): void
{
Schema::dropIfExists('auth_sessions');
}
};