74 lines
2.0 KiB
PHP
74 lines
2.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Auth;
|
|
|
|
use App\Models\PersonalAccessToken;
|
|
use App\Models\User;
|
|
use Hyperf\Context\Context;
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
|
|
class BearerTokenResolver
|
|
{
|
|
public const CONTEXT_KEY = '__bearer.access_token';
|
|
|
|
public static function resolve(ServerRequestInterface $request): ?User
|
|
{
|
|
$plain = self::extract($request);
|
|
if ($plain === null) {
|
|
return null;
|
|
}
|
|
|
|
$secret = str_contains($plain, '|') ? explode('|', $plain, 2)[1] : $plain;
|
|
$token = PersonalAccessToken::findByPlainToken($secret);
|
|
|
|
if ($token === null || ! $token->isActive()) {
|
|
return null;
|
|
}
|
|
|
|
$user = $token->tokenable()->first();
|
|
if (! $user instanceof User || ! $user->active) {
|
|
return null;
|
|
}
|
|
|
|
$ip = self::clientIp($request);
|
|
$token->forceFill([
|
|
'last_used_at' => now(),
|
|
'last_used_ip' => $ip,
|
|
])->save();
|
|
|
|
$user->withAccessToken($token);
|
|
Context::set(self::CONTEXT_KEY, $token);
|
|
|
|
return $user;
|
|
}
|
|
|
|
public static function current(): ?PersonalAccessToken
|
|
{
|
|
$token = Context::get(self::CONTEXT_KEY);
|
|
return $token instanceof PersonalAccessToken ? $token : null;
|
|
}
|
|
|
|
private static function extract(ServerRequestInterface $request): ?string
|
|
{
|
|
$header = $request->getHeaderLine('Authorization');
|
|
if ($header !== '' && stripos($header, 'Bearer ') === 0) {
|
|
return trim(substr($header, 7));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static function clientIp(ServerRequestInterface $request): string
|
|
{
|
|
foreach (['X-Forwarded-For', 'X-Real-IP'] as $h) {
|
|
$val = $request->getHeaderLine($h);
|
|
if ($val !== '') {
|
|
return trim(explode(',', $val)[0]);
|
|
}
|
|
}
|
|
$server = $request->getServerParams();
|
|
return $server['remote_addr'] ?? $server['REMOTE_ADDR'] ?? '0.0.0.0';
|
|
}
|
|
}
|