Passed
Push — master ( c97e91...9a636e )
by Alexandre
01:52
created

AuthorizationCodeFlow::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: Alexandre
5
 * Date: 07/03/2018
6
 * Time: 23:43
7
 */
8
9
namespace OAuth2\Extensions\PKCE\Flows;
10
11
12
use OAuth2\Endpoints\AuthorizationEndpoint;
13
use OAuth2\Endpoints\TokenEndpoint;
14
use OAuth2\Exceptions\OAuthException;
15
use OAuth2\Extensions\PKCE\Credentials\CodeChallenge;
16
use OAuth2\Extensions\PKCE\Storages\AuthorizationCodeStorageInterface;
17
use OAuth2\Roles\Clients\PublicClient;
18
19
/**
20
 * Class AuthorizationCodeFlow
21
 * @package OAuth2\Extensions\PKCE\Flows
22
 * @rfc https://tools.ietf.org/html/rfc7636
23
 */
24
class AuthorizationCodeFlow extends \OAuth2\Flows\AuthorizationCodeFlow
25
{
26
    /**
27
     * @var AuthorizationCodeStorageInterface
28
     */
29
    protected $authorizationCodeStorage;
30
31
    /**
32
     * AuthorizationCodeFlow constructor.
33
     * @param \OAuth2\Flows\AuthorizationCodeFlow $authorizationCodeFlow
34
     * @param AuthorizationCodeStorageInterface $authorizationCodeStorage
35
     */
36
    public function __construct(\OAuth2\Flows\AuthorizationCodeFlow $authorizationCodeFlow,
37
                                AuthorizationCodeStorageInterface $authorizationCodeStorage)
38
    {
39
        parent::__construct($authorizationCodeStorage);
0 ignored issues
show
Bug introduced by
The call to OAuth2\Flows\AuthorizationCodeFlow::__construct() has too few arguments starting with accessTokenStorage. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

39
        parent::/** @scrutinizer ignore-call */ 
40
                __construct($authorizationCodeStorage);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
40
        $this->authorizationCodeStorage = $authorizationCodeStorage;
41
    }
42
43
    /**
44
     * @param AuthorizationEndpoint $authorizationEndpoint
45
     * @param array                 $requestData
46
     * @return array
47
     * @throws OAuthException
48
     */
49
    function handleAuthorizationRequest(AuthorizationEndpoint $authorizationEndpoint, array $requestData): array
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
50
    {
51
        $authorizationCode = $this->createAuthorizationCode($authorizationEndpoint);
52
53
        if (empty($requestData['code_challenge'])) {
54
            if ($authorizationEndpoint->getClient() instanceof PublicClient) {
55
                throw new OAuthException('invalid_request',
56
                    'The request is missing the required parameter code_challenge for public clients',
57
                    'https://tools.ietf.org/html/rfc7636#section-4.4');
58
            }
59
        } else {
60
            $codeChallengeMethod = empty($requestData['code_challenge_method']) ? 'plain' : $requestData['code_challenge_method'];
61
            if (!in_array($codeChallengeMethod, ['plain', 'S256'])) {
62
                throw new OAuthException('invalid_request',
63
                    'The request includes the invalid parameter code_challenge_method. Supported : plain, S256',
64
                    'https://tools.ietf.org/html/rfc7636#section-4');
65
            }
66
67
            $codeChallenge = new CodeChallenge($requestData['code_challenge'], $codeChallengeMethod);
68
            $this->authorizationCodeStorage->associate($codeChallenge, $authorizationCode);
69
        }
70
71
        return $this->saveAndGetResult($authorizationCode);
72
    }
73
74
    /**
75
     * @param TokenEndpoint $tokenEndpoint
76
     * @param array $requestData
77
     * @return array
78
     * @throws OAuthException
79
     */
80
    public function handleAccessTokenRequest(TokenEndpoint $tokenEndpoint, array $requestData): array
81
    {
82
        if (empty($requestData['code'])) {
83
            throw new OAuthException('invalid_request',
84
                'The request is missing the required parameter code',
85
                'https://tools.ietf.org/html/rfc7636#section-4.4');
86
        }
87
        $code = $requestData['code'];
88
89
        $authorizationCode = $this->authorizationCodeStorage->find($code);
90
91
        /**
92
         * ensure that the authorization code was issued to the authenticated
93
         * confidential client, or if the client is public, ensure that the
94
         * code was issued to "client_id" in the request,
95
         */
96
        if (!$authorizationCode || $authorizationCode->getClientIdentifier() !== $tokenEndpoint->getClient()->getIdentifier()) {
97
            throw new OAuthException('invalid_grant',
98
                'The request includes the invalid parameter code',
99
                'https://tools.ietf.org/html/rfc7636#section-4.4');
100
        }
101
102
        $this->authorizationCodeStorage->revoke($code);
103
104
        /**
105
         * verify that the authorization code is valid
106
         */
107
        if ($authorizationCode->isExpired()) {
108
            throw new OAuthException('invalid_grant',
109
                'The request includes the invalid parameter code. The code has expired',
110
                'https://tools.ietf.org/html/rfc7636#section-4.4');
111
        }
112
113
        /**
114
         * ensure that the "redirect_uri" parameter is present if the
115
         * "redirect_uri" parameter was included in the initial authorization
116
         * request as described in Section 4.1.1, and if included ensure that
117
         * their values are identical.
118
         */
119
        if ($authorizationCode->getRedirectUri()) {
120
            if (empty($requestData['redirect_uri'])) {
121
                throw new OAuthException('invalid_request',
122
                    'The request is missing the required parameter redirect_uri',
123
                    'https://tools.ietf.org/html/rfc7636#section-4.1');
124
            }
125
            if ($requestData['redirect_uri'] !== $authorizationCode->getRedirectUri()) {
126
                throw new OAuthException('invalid_request',
127
                    'The request includes the invalid parameter redirect_uri',
128
                    'https://tools.ietf.org/html/rfc7636#section-4.1');
129
            }
130
        }
131
132
        $codeChallenge = $this->authorizationCodeStorage->getCodeChallenge($authorizationCode);
133
134
        if ($codeChallenge && $codeChallenge->getCodeChallenge()) {
135
            if (empty($requestData['code_verifier'])) {
136
                throw new OAuthException('invalid_request',
137
                    'The request is missing the required parameter code_verifier',
138
                    'https://tools.ietf.org/html/rfc7636#section-4.4');
139
            }
140
141
            if ($codeChallenge->getCodeChallengeMethod() === 'S256') {
142
                /**
143
                 * If the "code_challenge_method" from Section 4.3 was "S256", the
144
                 * received "code_verifier" is hashed by SHA-256, base64url-encoded, and
145
                 * then compared to the "code_challenge", i.e.:
146
                 */
147
                $hashedCodeVerifier = self::base64url_encode(hash('sha256', $requestData['code_verifier']));
148
            } else {
149
                /**
150
                 * If the "code_challenge_method" from Section 4.3 was "plain", they are
151
                 * compared directly, i.e.:
152
                 */
153
                $hashedCodeVerifier = $requestData['code_verifier'];
154
            }
155
156
            /**
157
             * If the values are equal, the token endpoint MUST continue processing
158
             * as normal (as defined by OAuth 2.0 [RFC6749]).  If the values are not
159
             * equal, an error response indicating "invalid_grant" as described in
160
             * Section 5.2 of [RFC6749] MUST be returned.
161
             */
162
            if ($hashedCodeVerifier !== $codeChallenge->getCodeChallenge()) {
163
                throw new OAuthException('invalid_grant',
164
                    'The request includes the invalid parameter code_verifier',
165
                    'https://tools.ietf.org/html/rfc7636#section-4.4');
166
            }
167
        }
168
169
        return $tokenEndpoint->issueTokens($authorizationCode->getScope(),
0 ignored issues
show
Bug introduced by
The method issueTokens() does not exist on OAuth2\Endpoints\TokenEndpoint. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

169
        return $tokenEndpoint->/** @scrutinizer ignore-call */ issueTokens($authorizationCode->getScope(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
170
            $authorizationCode->getResourceOwnerIdentifier(), $authorizationCode->getCode());
171
    }
172
173
    /**
174
     * @param $data
175
     * @return string
176
     * @src https://gist.github.com/nathggns/6652997
177
     */
178
    public static function base64url_encode($data)
179
    {
180
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
181
    }
182
183
    /**
184
     * @param      $data
185
     * @param bool $pad
186
     * @return bool|string
187
     * @src https://gist.github.com/nathggns/6652997
188
     */
189
    public static function base64url_decode($data, $pad = false)
190
    {
191
        $data = strtr($data, '-_', '+/');
192
        if ($pad) {
193
            $data = str_pad($data, strlen($data) + (4 - strlen($data) % 4) % 4);
194
        }
195
        return base64_decode($data);
196
    }
197
198
}