AuthTokenMiddleware   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 102
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 51
c 2
b 0
f 0
dl 0
loc 102
ccs 0
cts 55
cp 0
rs 10
wmc 12

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getTokenFromRequest() 0 16 4
A __construct() 0 4 1
B process() 0 43 7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Middleware;
6
7
use App\Service\Token\Exception\TokenValidationExceptionInterface;
8
use App\Service\Token\TokenManager;
9
use Fig\Http\Message\StatusCodeInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Http\Message\ServerRequestInterface;
12
use Psr\Http\Server\MiddlewareInterface;
13
use Psr\Http\Server\RequestHandlerInterface;
14
use Zend\Diactoros\Response\JsonResponse;
15
16
class AuthTokenMiddleware implements MiddlewareInterface
17
{
18
    public const DEFAULT_OPTIONS = [
19
        self::OPTION_ALLOW_INSECURE_HTTP => false,
20
        self::OPTION_RELAXED_HOSTS       => [],
21
        self::OPTION_HTTP_HEADER         => 'Authorization',
22
        self::OPTION_HTTP_HEADER_PREFIX  => 'Bearer',
23
    ];
24
25
    public const OPTION_ALLOW_INSECURE_HTTP = 'allow_insecure_http';
26
    public const OPTION_RELAXED_HOSTS       = 'relaxed_hosts';
27
28
    /**
29
     * @var string
30
     */
31
    public const OPTION_HTTP_HEADER = 'httpHeader';
32
33
    /**
34
     * @var string
35
     */
36
    public const OPTION_HTTP_HEADER_PREFIX = 'httpHeaderPrefix';
37
38
    /**
39
     * @var mixed[]
40
     */
41
    private $options = [];
42
43
    /**
44
     * @var TokenManager
45
     */
46
    private $tokenManager;
47
48
    /**
49
     * @param mixed[] $options
50
     */
51
    public function __construct(TokenManager $tokenManager, array $options = [])
52
    {
53
        $this->tokenManager = $tokenManager;
54
        $this->options      = array_merge(self::DEFAULT_OPTIONS, $options);
55
    }
56
57
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
58
    {
59
        // 1. Check for secure scheme (with exception of relaxed_hosts)
60
        $scheme = mb_strtolower($request->getUri()->getScheme());
61
62
        if ($this->options['allow_insecure_http'] !== true && $scheme !== 'https') {
63
            $host          = $request->getUri()->getHost();
64
            $relaxed_hosts = (array) $this->options['relaxed_hosts'];
65
            if (!in_array($host, $relaxed_hosts, true)) {
66
                throw new Exception\InsecureSchemeException(sprintf(
67
                    'Insecure scheme (%s) denied by configuration.',
68
                    $scheme
69
                ));
70
            }
71
        }
72
73
        // 2. Fetch token from server request
74
75
        $plainToken = $this->getTokenFromRequest($request);
76
77
        // 3. Validate the token
78
        if ($plainToken !== null) {
79
            try {
80
                $token = $this->tokenManager->getValidatedToken($plainToken);
81
82
                return $handler->handle($request->withAttribute(self::class, $token));
83
            } catch (TokenValidationExceptionInterface $e) {
84
                return (new JsonResponse([
85
                    'valid'  => false,
86
                    'reason' => $e->getReason(),
87
                ]))->withStatus($e->getStatusCode());
88
            } catch (\Throwable $e) {
89
                return (new JsonResponse([
90
                    'valid'  => false,
91
                    'reason' => 'Unknown reason',
92
                ]))->withStatus(StatusCodeInterface::STATUS_UNAUTHORIZED);
93
            }
94
        }
95
96
        return (new JsonResponse([
97
            'valid'  => false,
98
            'reason' => 'No token provided',
99
        ]))->withStatus(StatusCodeInterface::STATUS_UNAUTHORIZED);
100
    }
101
102
    public function getTokenFromRequest(ServerRequestInterface $request): ?string
103
    {
104
        $headerPrefix = $this->options[self::OPTION_HTTP_HEADER_PREFIX];
105
        $headers      = $request->getHeader($this->options[self::OPTION_HTTP_HEADER]);
106
        $tokenString  = null;
107
        foreach ($headers as $header) {
108
            if ($headerPrefix !== '') {
109
                if (mb_strpos($header, $headerPrefix) === 0) {
110
                    $tokenString = trim(str_replace($headerPrefix, '', $header));
111
                }
112
            } else {
113
                $tokenString = trim($header);
114
            }
115
        }
116
117
        return $tokenString;
118
    }
119
}
120