add SessionTimeout logic
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Auth;
|
||||
|
||||
use App\Http\Controllers\Api\BaseApiController;
|
||||
use App\Http\Requests\Auth\SessionTimeoutCheckRequest;
|
||||
use App\Http\Requests\Auth\SessionTimeoutConfigRequest;
|
||||
use App\Http\Requests\Auth\SessionTimeoutPingRequest;
|
||||
use App\Http\Resources\Auth\SessionTimeoutConfigResource;
|
||||
use App\Http\Resources\Auth\SessionTimeoutStatusResource;
|
||||
use App\Services\Auth\SessionTimeoutConfigService;
|
||||
use App\Services\Auth\SessionTimeoutService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class SessionTimeoutController extends BaseApiController
|
||||
{
|
||||
public function __construct(
|
||||
private SessionTimeoutConfigService $configService,
|
||||
private SessionTimeoutService $timeoutService
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function config(SessionTimeoutConfigRequest $request): JsonResponse
|
||||
{
|
||||
$config = $this->configService->config();
|
||||
|
||||
return $this->success([
|
||||
'config' => new SessionTimeoutConfigResource($config),
|
||||
]);
|
||||
}
|
||||
|
||||
public function check(SessionTimeoutCheckRequest $request): JsonResponse
|
||||
{
|
||||
$result = $this->timeoutService->checkTimeout();
|
||||
|
||||
if (($result['status'] ?? '') === 'expired') {
|
||||
return $this->error(
|
||||
$result['message'] ?? 'Session expired.',
|
||||
Response::HTTP_UNAUTHORIZED,
|
||||
(new SessionTimeoutStatusResource($result))->toArray($request)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'session' => new SessionTimeoutStatusResource($result),
|
||||
]);
|
||||
}
|
||||
|
||||
public function ping(SessionTimeoutPingRequest $request): JsonResponse
|
||||
{
|
||||
$result = $this->timeoutService->pingActivity();
|
||||
|
||||
if (($result['status'] ?? '') === 'expired') {
|
||||
return $this->error(
|
||||
$result['message'] ?? 'Session expired.',
|
||||
Response::HTTP_UNAUTHORIZED,
|
||||
(new SessionTimeoutStatusResource($result))->toArray($request)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'session' => new SessionTimeoutStatusResource($result),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use App\Http\Requests\ApiFormRequest;
|
||||
|
||||
class SessionTimeoutCheckRequest extends ApiFormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use App\Http\Requests\ApiFormRequest;
|
||||
|
||||
class SessionTimeoutConfigRequest extends ApiFormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use App\Http\Requests\ApiFormRequest;
|
||||
|
||||
class SessionTimeoutPingRequest extends ApiFormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources\Auth;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class SessionTimeoutConfigResource extends JsonResource
|
||||
{
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'timeout' => (int) ($this['timeout'] ?? 0),
|
||||
'warning_time' => (int) ($this['warning_time'] ?? 0),
|
||||
'check_interval' => (int) ($this['check_interval'] ?? 0),
|
||||
'logout_url' => (string) ($this['logout_url'] ?? ''),
|
||||
'keep_alive_url' => (string) ($this['keep_alive_url'] ?? ''),
|
||||
'check_url' => (string) ($this['check_url'] ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources\Auth;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class SessionTimeoutStatusResource extends JsonResource
|
||||
{
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'status' => (string) ($this['status'] ?? ''),
|
||||
'time_remaining' => (int) ($this['time_remaining'] ?? 0),
|
||||
'redirect' => $this['redirect'] ?? null,
|
||||
'message' => $this['message'] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Auth;
|
||||
|
||||
use Illuminate\Session\Store;
|
||||
|
||||
class SessionActivityService
|
||||
{
|
||||
public function __construct(private Store $session)
|
||||
{
|
||||
}
|
||||
|
||||
public function hasLastActivity(): bool
|
||||
{
|
||||
return $this->session->has('last_activity');
|
||||
}
|
||||
|
||||
public function getLastActivity(): ?int
|
||||
{
|
||||
$value = $this->session->get('last_activity');
|
||||
return $value !== null ? (int) $value : null;
|
||||
}
|
||||
|
||||
public function setLastActivity(int $timestamp): void
|
||||
{
|
||||
$this->session->put('last_activity', $timestamp);
|
||||
}
|
||||
|
||||
public function clearLastActivity(): void
|
||||
{
|
||||
$this->session->forget('last_activity');
|
||||
}
|
||||
|
||||
public function invalidate(): void
|
||||
{
|
||||
$this->session->invalidate();
|
||||
$this->session->regenerateToken();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Auth;
|
||||
|
||||
use App\Config\SessionTimeout;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
class SessionTimeoutConfigService
|
||||
{
|
||||
public function config(): array
|
||||
{
|
||||
return [
|
||||
'timeout' => SessionTimeout::TIMEOUT_DURATION,
|
||||
'warning_time' => SessionTimeout::WARNING_THRESHOLD,
|
||||
'check_interval' => SessionTimeout::CHECK_INTERVAL,
|
||||
'logout_url' => URL::to('/auth/logout'),
|
||||
'keep_alive_url' => route('session.timeout.ping'),
|
||||
'check_url' => route('session.timeout.check'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Auth;
|
||||
|
||||
use App\Config\SessionTimeout;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
|
||||
class SessionTimeoutService
|
||||
{
|
||||
public function __construct(
|
||||
private SessionActivityService $activityService
|
||||
) {
|
||||
}
|
||||
|
||||
public function checkTimeout(): array
|
||||
{
|
||||
if (!$this->activityService->hasLastActivity()) {
|
||||
return $this->expireSession();
|
||||
}
|
||||
|
||||
$lastActivity = (int) $this->activityService->getLastActivity();
|
||||
$elapsed = time() - $lastActivity;
|
||||
|
||||
if ($elapsed > SessionTimeout::TIMEOUT_DURATION) {
|
||||
return $this->expireSession();
|
||||
}
|
||||
|
||||
if ($elapsed > SessionTimeout::WARNING_THRESHOLD) {
|
||||
return [
|
||||
'status' => 'warning',
|
||||
'time_remaining' => SessionTimeout::TIMEOUT_DURATION - $elapsed,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => 'active',
|
||||
'time_remaining' => SessionTimeout::TIMEOUT_DURATION - $elapsed,
|
||||
];
|
||||
}
|
||||
|
||||
public function pingActivity(): array
|
||||
{
|
||||
if (
|
||||
!$this->activityService->hasLastActivity()
|
||||
|| (time() - (int) $this->activityService->getLastActivity() > SessionTimeout::TIMEOUT_DURATION)
|
||||
) {
|
||||
return $this->expireSession();
|
||||
}
|
||||
|
||||
$this->activityService->setLastActivity(time());
|
||||
|
||||
return [
|
||||
'status' => 'active',
|
||||
'time_remaining' => SessionTimeout::TIMEOUT_DURATION,
|
||||
];
|
||||
}
|
||||
|
||||
private function expireSession(): array
|
||||
{
|
||||
Log::info('Session expired due to inactivity.');
|
||||
$this->activityService->clearLastActivity();
|
||||
$this->activityService->invalidate();
|
||||
|
||||
return [
|
||||
'status' => 'expired',
|
||||
'redirect' => URL::to('/login'),
|
||||
'message' => 'Your session has expired due to inactivity.',
|
||||
'time_remaining' => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user