Passed
Push — master ( 3a86cf...ce8c92 )
by Alexander
01:22
created

HttpBasic::isBasicToken()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Auth\Method;
6
7
use Psr\Http\Message\ResponseInterface;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Yiisoft\Auth\AuthInterface;
10
use Yiisoft\Auth\IdentityInterface;
11
use Yiisoft\Auth\IdentityRepositoryInterface;
12
13
/**
14
 * HttpBasicAuth supports the HTTP Basic authentication method.
15
 *
16
 * > Tip: In case authentication does not work like expected, make sure your web server passes
17
 * username and password to `$request->getServerParams()['PHP_AUTH_USER']` and `$request->getServerParams()['PHP_AUTH_PW']` variables.
18
 * If you are using Apache with PHP-CGI, you might need to add this line to your `.htaccess` file:
19
 * ```
20
 * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
21
 * ```
22
 */
23
final class HttpBasic implements AuthInterface
24
{
25
    /**
26
     * @var string the HTTP authentication realm
27
     */
28
    private string $realm = 'api';
29
    /**
30
     * @var callable a PHP callable that will authenticate the user with the HTTP basic auth information.
31
     * The callable receives a username and a password as its parameters. It should return an identity object
32
     * that matches the username and password. Null should be returned if there is no such identity.
33
     * The callable will be called only if current user is not authenticated.
34
     *
35
     * If this property is not set, the username information will be considered as an access token
36
     * while the password information will be ignored. The {@see \Yiisoft\Yii\Web\User\IdentityRepositoryInterface::findIdentityByToken()}
37
     * method will be called to authenticate and login the user.
38
     */
39
    private $auth;
40
41
    private IdentityRepositoryInterface $identityRepository;
42
43 10
    public function __construct(IdentityRepositoryInterface $identityRepository)
44
    {
45 10
        $this->identityRepository = $identityRepository;
46 10
    }
47
48 8
    public function authenticate(ServerRequestInterface $request): ?IdentityInterface
49
    {
50 8
        [$username, $password] = $this->getAuthCredentials($request);
51
52 8
        if ($this->auth && ($username !== null || $password !== null)) {
53 2
            return \call_user_func($this->auth, $username, $password);
54
        }
55
56 6
        if ($username !== null) {
57 4
            return $this->identityRepository->findIdentityByToken($username, get_class($this));
58
        }
59
60 2
        return null;
61
    }
62
63 2
    public function challenge(ResponseInterface $response): ResponseInterface
64
    {
65 2
        return $response->withHeader('WWW-Authenticate', "Basic realm=\"{$this->realm}\"");
66
    }
67
68 2
    public function setAuth(callable $auth): void
69
    {
70 2
        $this->auth = $auth;
71 2
    }
72
73 1
    public function setRealm(string $realm): void
74
    {
75 1
        $this->realm = $realm;
76 1
    }
77
78 8
    private function getAuthCredentials(ServerRequestInterface $request): array
79
    {
80 8
        $username = $request->getServerParams()['PHP_AUTH_USER'] ?? null;
81 8
        $password = $request->getServerParams()['PHP_AUTH_PW'] ?? null;
82 8
        if ($username !== null || $password !== null) {
83 4
            return [$username, $password];
84
        }
85
86
        /*
87
         * Apache with php-cgi does not pass HTTP Basic authentication to PHP by default.
88
         * To make it work, add the following line to to your .htaccess file:
89
         *
90
         * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
91
         */
92 4
        $token = $this->getTokenFromHeaders($request);
93 4
        if ($token !== null && $this->isBasicToken($token)) {
94 3
            $credentials = $this->extractCredentialsFromHeader($token);
95 3
            if (\count($credentials) < 2) {
96 1
                return [$credentials[0], null];
97
            }
98
99 2
            return $credentials;
100
        }
101
102 1
        return [null, null];
103
    }
104
105 4
    private function getTokenFromHeaders(ServerRequestInterface $request): ?string
106
    {
107 4
        $header = $request->getHeaderLine('Authorization');
108 4
        if (!empty($header)) {
109 2
            return $header;
110
        }
111
112 2
        return $request->getServerParams()['REDIRECT_HTTP_AUTHORIZATION'] ?? null;
113
    }
114
115 3
    private function extractCredentialsFromHeader(string $authToken): array
116
    {
117 3
        return array_map(
118 3
            fn ($value) => $value === '' ? null : $value,
119 3
            explode(':', base64_decode(mb_substr($authToken, 6)), 2)
120
        );
121
    }
122
123 3
    private function isBasicToken(string $token): bool
124
    {
125 3
        return strncasecmp($token, 'basic', 5) === 0;
126
    }
127
}
128