BasicClientAuthenticationTrait::determineClient()   D
last analyzed

Complexity

Conditions 21
Paths 53

Size

Total Lines 85

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 43
CRAP Score 21

Importance

Changes 0
Metric Value
dl 0
loc 85
ccs 43
cts 43
cp 1
rs 4.1666
c 0
b 0
f 0
cc 21
nc 53
nop 4
crap 21

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
2
3
namespace Limoncello\Passport\Traits;
4
5
/**
6
 * Copyright 2015-2019 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use Limoncello\OAuthServer\Contracts\ClientInterface;
22
use Limoncello\OAuthServer\Exceptions\OAuthTokenBodyException;
23
use Limoncello\Passport\Contracts\PassportServerIntegrationInterface;
24
use Psr\Http\Message\ServerRequestInterface;
25
use function array_key_exists;
26
use function assert;
27
use function base64_decode;
28
use function count;
29
use function explode;
30
use function is_string;
31
use function substr;
32
33
/**
34
 * @package Limoncello\Passport
35
 */
36
trait BasicClientAuthenticationTrait
37
{
38
    /**
39
     * @param PassportServerIntegrationInterface $integration
40
     * @param ServerRequestInterface             $request
41
     * @param array                              $parameters
42
     * @param string                             $realm
43
     *
44
     * @return ClientInterface|null
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use \Limoncello\Passport\Con...es\ClientInterface|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
45
     *
46
     * @SuppressWarnings(PHPMD.ElseExpression)
47
     * @SuppressWarnings(PHPMD.NPathComplexity)
48
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
49
     */
50 13
    protected function determineClient(
51
        PassportServerIntegrationInterface $integration,
52
        ServerRequestInterface $request,
53
        array $parameters,
54
        $realm = 'OAuth'
55
    ): ?ClientInterface {
56
        // A client may use Basic authentication.
57
        //
58
        // Or
59
        //
60
        // A client MAY use the "client_id" request parameter to identify itself
61
        // when sending requests to the token endpoint.
62
        // @link https://tools.ietf.org/html/rfc6749#section-3.2.1
63
64 13
        $authorizationHeader = $request->getHeader('Authorization');
65
66
        // try to parse `Authorization` header for client ID and credentials
67 13
        $clientId          = null;
68 13
        $clientCredentials = null;
69 13
        $errorHeaders      = ['WWW-Authenticate' => 'Basic realm="' . $realm . '"'];
70 13
        if (empty($headerArray = $authorizationHeader) === false) {
71 6
            $errorCode = OAuthTokenBodyException::ERROR_INVALID_CLIENT;
72 6
            if (empty($authHeader = $headerArray[0]) === true ||
73 6
                ($tokenPos = strpos($authHeader, 'Basic ')) === false ||
74 6
                $tokenPos !== 0 ||
75 6
                ($authValue = substr($authHeader, 6)) === '' ||
76 6
                $authValue === false ||
77 6
                ($decodedValue = base64_decode($authValue, true)) === false ||
78 6
                ($idAndCredentials = explode(':', $decodedValue, 2)) === false
79
            ) {
80 1
                throw new OAuthTokenBodyException($errorCode, null, 401, $errorHeaders);
81
            }
82 5
            $headerPartsCount = count($idAndCredentials);
83
            switch ($headerPartsCount) {
84 5
                case 1:
85 2
                    $clientId = $idAndCredentials[0];
86 2
                    break;
87 3
                case 2:
88
                default:
89 3
                    $clientId          = $idAndCredentials[0];
90 3
                    $clientCredentials = $idAndCredentials[1];
91 3
                    break;
92
            }
93
        }
94
95
        // check if client ID was specified in parameters it should match
96 12
        if (array_key_exists('client_id', $parameters) === true &&
97 12
            is_string($value = $parameters['client_id']) === true
98
        ) {
99 5
            if ($clientId !== null && $clientId !== $value) {
100 1
                $errorCode = OAuthTokenBodyException::ERROR_INVALID_REQUEST;
101 1
                throw new OAuthTokenBodyException($errorCode, null, 400, $errorHeaders);
102
            }
103
104 4
            $clientId = $value;
105 4
            unset($value);
106
        }
107
108
        // when we are here we know if any client ID and credentials were given
109
110 11
        $client = null;
111 11
        if ($clientId !== null) {
112 8
            assert(is_string($clientId));
113 8
            $errorCode = OAuthTokenBodyException::ERROR_INVALID_CLIENT;
114 8
            if (($client = $integration->getClientRepository()->read($clientId)) === null) {
115 1
                throw new OAuthTokenBodyException($errorCode, null, 401, $errorHeaders);
116
            }
117
118
            // check credentials
119 7
            if ($clientCredentials !== null) {
120 2
                assert(is_string($clientCredentials));
121
                // we got the password
122 2
                if ($integration->verifyClientCredentials($client, $clientCredentials) === false) {
123 2
                    throw new OAuthTokenBodyException($errorCode, null, 401, $errorHeaders);
124
                }
125
            } else {
126
                // no password provided
127 5
                if ($client->isConfidential() === true || $client->hasCredentials() === true) {
128 1
                    throw new OAuthTokenBodyException($errorCode, null, 401, $errorHeaders);
129
                }
130
            }
131
        }
132
133 8
        return $client;
134
    }
135
}
136