BasicAuthProcessor::decodeCredentials()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 1
dl 0
loc 9
ccs 0
cts 8
cp 0
crap 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Koded\Framework\Auth;
4
5
use Koded\Http\{HTTPError, HTTPUnauthorized};
6
use Throwable;
7
use function base64_decode;
8
use function count;
9
use function join;
10
use function mb_split;
11
use function strtolower;
12
use function trim;
13
14
/**
15
 * Implements HTTP Basic Authentication
16
 * <http://tools.ietf.org/html/rfc2617>
17
 *
18
 * Clients should authenticate by passing the `base64` encoded credentials
19
 * `username:password` in the `Authorization` HTTP header, prepended with the
20
 * string specified in the setting `auth_header_prefix`. For example:
21
 *
22
 *      Authorization: Basic aGVsbG86d29ybGQ=
23
 */
24
class BasicAuthProcessor implements AuthProcessor
25
{
26
    public function authenticate(AuthBackend $backend, string $credentials): ?object
27
    {
28
        try {
29
            return $backend(
30
                $this->getTokenPrefix(),
31
                join(':', $this->decodeCredentials($this->extractCredentials($credentials)))
32
            );
33
        } catch (HTTPError $e) {
34
            throw $e;
35
        } catch (Throwable $e) {
36
            throw new HTTPUnauthorized(
37
                title: __('Authorization failed'),
38
                detail: $e->getMessage()
39
            );
40
        }
41
    }
42
43
    public function getTokenPrefix(): string
44
    {
45
        return 'Basic';
46
    }
47
48
    protected function extractCredentials(string $credentials): string
49
    {
50
        $credentials = trim($credentials);
51
        empty($credentials) and throw new HTTPUnauthorized(
52
            title: __('Invalid authorization credentials'),
53
            detail: __('The authorization header is missing'),
54
            type: 'https://kodedphp.github.io/auth/header',
55
            headers: ['WWW-Authenticate' => $this->getTokenPrefix()]
56
        );
57
        $parts = mb_split('\s', $credentials);
58
59
        (strtolower($parts[0]) !== strtolower($this->getTokenPrefix())) and throw new HTTPUnauthorized(
60
            title: __('Authorization failed'),
61
            detail: __('Authorization header must start with %s', [$this->getTokenPrefix()]),
62
            type: 'https://kodedphp.github.io/auth/format',
63
            headers: ['WWW-Authenticate' => $this->getTokenPrefix()]
64
        );
65
        1 === count($parts) and throw new HTTPUnauthorized(
66
            title: __('Authorization failed'),
67
            detail: __('Missing authorization value'),
68
            type: 'https://kodedphp.github.io/auth/value',
69
            headers: ['WWW-Authenticate' => $this->getTokenPrefix()]
70
        );
71
        2 < count($parts) and throw new HTTPUnauthorized(
72
            title: __('Authorization failed'),
73
            detail: __('Authorization header contains extra values'),
74
            type: 'https://kodedphp.github.io/auth/format'
75
        );
76
        return $parts[1];
77
    }
78
79
    private function decodeCredentials(string $secret): array
80
    {
81
        $decoded = mb_split(':', base64_decode($secret, true));
82
        (false === $decoded || 2 !== count($decoded)) and throw new HTTPUnauthorized(
83
            title: __('Authorization failed'),
84
            detail: __('Failed to process the authorization credentials'),
85
            type: 'https://kodedphp.github.io/auth/credentials'
86
        );
87
        return $decoded;
88
    }
89
}
90