Failed Conditions
Push — master ( 7c66af...b69c62 )
by Sébastien
02:24
created

AuthTokenMiddleware::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
ccs 0
cts 4
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Middleware;
6
7
use App\Service\Exception\TokenValidationExceptionInterface;
8
use App\Service\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         => 'Authentication',
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
    /*
58
     * @return ResponseInterface|RedirectResponse
0 ignored issues
show
Bug introduced by
The type App\Middleware\RedirectResponse was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
59
     */
60
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
61
    {
62
        // 1. Check for secure scheme (with exception of relaxed_hosts)
63
        $scheme = mb_strtolower($request->getUri()->getScheme());
64
65
        if ($this->options['allow_insecure_http'] !== true && $scheme !== 'https') {
66
            $host          = $request->getUri()->getHost();
67
            $relaxed_hosts = (array) $this->options['relaxed_hosts'];
68
            if (!in_array($host, $relaxed_hosts, true)) {
69
                throw new Exception\InsecureSchemeException(sprintf(
70
                    'Insecure scheme (%s) denied by configuration.',
71
                    $scheme
72
                ));
73
            }
74
        }
75
76
        // 2. Fetch token from server request
77
78
        $plainToken = $this->getTokenFromRequest($request);
79
80
        // 3. Validate the token
81
        if ($plainToken !== null) {
82
            try {
83
                $token = $this->tokenManager->getValidatedToken($plainToken);
84
85
                return $handler->handle($request->withAttribute(self::class, $token));
86
            } catch (TokenValidationExceptionInterface $e) {
87
                return (new JsonResponse([
88
                    'valid'  => false,
89
                    'reason' => $e->getReason(),
90
                ]))->withStatus($e->getStatusCode());
91
            } catch (\Throwable $e) {
92
                return (new JsonResponse([
93
                    'valid'  => false,
94
                    'reason' => 'Unknown reason',
95
                ]))->withStatus(StatusCodeInterface::STATUS_UNAUTHORIZED);
96
            }
97
        } else {
98
            $message = 'No token provided';
0 ignored issues
show
Unused Code introduced by
The assignment to $message is dead and can be removed.
Loading history...
99
        }
100
101
        return (new JsonResponse([
102
            'valid'  => false,
103
            'reason' => 'No token provided',
104
        ]))->withStatus(StatusCodeInterface::STATUS_UNAUTHORIZED);
105
    }
106
107
    public function getTokenFromRequest(ServerRequestInterface $request): ?string
108
    {
109
        $headerPrefix = $this->options[self::OPTION_HTTP_HEADER_PREFIX];
110
        $headers      = $request->getHeader($this->options[self::OPTION_HTTP_HEADER]);
111
        $tokenString  = null;
112
        foreach ($headers as $header) {
113
            if ($headerPrefix !== '') {
114
                if (mb_strpos($header, $headerPrefix) === 0) {
115
                    $tokenString = trim(str_replace($headerPrefix, '', $header));
116
                }
117
            } else {
118
                $tokenString = trim($header);
119
            }
120
        }
121
122
        return $tokenString;
123
    }
124
}
125