AzuraCast/src/Middleware/Auth/ApiAuth.php

112 lines
3.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Middleware\Auth;
use App\Acl;
use App\Auth;
use App\Entity;
use App\Environment;
use App\Exception\CsrfValidationException;
use App\Http\ServerRequest;
use App\Session\Csrf;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class ApiAuth extends AbstractAuth
{
public const API_CSRF_NAMESPACE = 'api';
public function __construct(
protected Entity\Repository\UserRepository $userRepo,
protected Entity\Repository\ApiKeyRepository $apiKeyRepo,
Entity\Repository\SettingsRepository $settingsRepo,
Environment $environment,
Acl $acl
) {
parent::__construct($settingsRepo, $environment, $acl);
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
// Initialize the Auth for this request.
$user = $this->getApiUser($request);
$request = $request->withAttribute(ServerRequest::ATTR_USER, $user);
return parent::process($request, $handler);
}
protected function getApiUser(ServerRequestInterface $request): ?Entity\User
{
$apiKey = $this->getApiKey($request);
if (!empty($apiKey)) {
$apiUser = $this->apiKeyRepo->authenticate($apiKey);
if (null !== $apiUser) {
return $apiUser;
}
}
// Fallback to session login if available.
$csrfKey = $request->getHeaderLine('X-API-CSRF');
if (empty($csrfKey) && !$this->environment->isTesting()) {
return null;
}
$auth = new Auth(
userRepo: $this->userRepo,
session: $request->getAttribute(ServerRequest::ATTR_SESSION),
environment: $this->environment,
);
if ($auth->isLoggedIn()) {
$csrf = $request->getAttribute(ServerRequest::ATTR_SESSION_CSRF);
if ($csrf instanceof Csrf) {
try {
$csrf->verify($csrfKey, self::API_CSRF_NAMESPACE);
return $auth->getLoggedInUser();
} catch (CsrfValidationException) {
}
}
}
return null;
}
protected function getApiKey(ServerRequestInterface $request): ?string
{
// Check authorization header
$auth_headers = $request->getHeader('Authorization');
$auth_header = $auth_headers[0] ?? '';
if (preg_match("/Bearer\s+(.*)$/i", $auth_header, $matches)) {
return $matches[1];
}
// Check API key header
$api_key_headers = $request->getHeader('X-API-Key');
if (!empty($api_key_headers[0])) {
return $api_key_headers[0];
}
// Check cookies
$cookieParams = $request->getCookieParams();
if (!empty($cookieParams['token'])) {
return $cookieParams['token'];
}
// Check URL parameters as last resort
$queryParams = $request->getQueryParams();
$queryApiKey = $queryParams['api_key'] ?? null;
if (!empty($queryApiKey)) {
return $queryApiKey;
}
return null;
}
}