Completed
Push — master ( fec6f1...fc6f01 )
by Alexandre
03:31
created

AuthorizationEndpoint::verify()   B

Complexity

Conditions 8
Paths 22

Size

Total Lines 75

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 22
nop 1
dl 0
loc 75
rs 7.301
c 0
b 0
f 0

How to fix   Long Method   

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
2
/**
3
 * Created by PhpStorm.
4
 * User: Alexandre
5
 * Date: 18/02/2018
6
 * Time: 18:14
7
 */
8
9
namespace OAuth2\Endpoints;
10
11
12
use GuzzleHttp\Psr7\Response;
13
use OAuth2\Endpoints\Authorization\AuthorizationRequest;
14
use OAuth2\Endpoints\Authorization\AuthorizationRequestBuilder;
15
use OAuth2\Exceptions\InvalidAuthorizationRequest;
16
use OAuth2\Exceptions\InvalidRequestMethod;
17
use OAuth2\Exceptions\OAuthException;
18
use OAuth2\Roles\AuthorizationServer\EndUserInterface;
19
use Psr\Http\Message\ResponseInterface;
20
use Psr\Http\Message\ServerRequestInterface;
21
22
23
/**
24
 * Class AuthorizationEndpoint
25
 * @package OAuth2\Endpoints
26
 *
27
 * @see https://tools.ietf.org/html/rfc6749#section-3.1
28
 * The authorization endpoint is used to interact with the resource
29
 * owner and obtain an authorization grant.  The authorization server
30
 * MUST first verify the identity of the resource owner.  The way in
31
 * which the authorization server authenticates the resource owner
32
 * (e.g., username and password login, session cookies) is beyond the
33
 * scope of this specification.
34
 *
35
 * The means through which the client obtains the location of the
36
 * authorization endpoint are beyond the scope of this specification,
37
 * but the location is typically provided in the service documentation.
38
 *
39
 * The endpoint URI MAY include an "application/x-www-form-urlencoded"
40
 * formatted (per Appendix B) query component ([RFC3986] Section 3.4),
41
 * which MUST be retained when adding additional query parameters.  The
42
 * endpoint URI MUST NOT include a fragment component.
43
 *
44
 * Since requests to the authorization endpoint result in user
45
 * authentication and the transmission of clear-text credentials (in the
46
 * HTTP response), the authorization server MUST require the use of TLS
47
 * as described in Section 1.6 when sending requests to the
48
 * authorization endpoint.
49
 *
50
 * The authorization server MUST support the use of the HTTP "GET"
51
 * method [RFC2616] for the authorization endpoint and MAY support the
52
 * use of the "POST" method as well.
53
 *
54
 * Parameters sent without a value MUST be treated as if they were
55
 * omitted from the request.  The authorization server MUST ignore
56
 * unrecognized request parameters.  Request and response parameters
57
 * MUST NOT be included more than once.
58
 */
59
class AuthorizationEndpoint implements EndpointInterface
60
{
61
    /**
62
     * @var AuthorizationRequestBuilder
63
     */
64
    private $authorizationRequestBuilder;
65
    /**
66
     * @var AuthorizationRequest|null
67
     */
68
    private $authorizationRequest = null;
69
    /**
70
     * @var EndUserInterface
71
     */
72
    private $authorizationServerEndUser;
73
74
    public function __construct(AuthorizationRequestBuilder $authorizationRequestBuilder,
75
                                EndUserInterface $authorizationServerEndUser)
76
    {
77
        $this->authorizationRequestBuilder = $authorizationRequestBuilder;
78
        $this->authorizationServerEndUser = $authorizationServerEndUser;
79
    }
80
81
    public function verify(ServerRequestInterface $request): ?ResponseInterface
82
    {
83
        try {
84
            if ($response = $this->verifyResourceOwner()) {
85
                return $response;
86
            }
87
88
            $this->authorizationRequest = $this->authorizationRequestBuilder
89
                ->build($request, $this->authorizationServerEndUser->getAuthenticatedResourceOwner());
90
        } catch (InvalidRequestMethod $e) {
91
            return new Response(405);
92
        } catch (OAuthException $e) {
93
            /**
94
             * @see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
95
             * If the request fails due to a missing, invalid, or mismatching
96
             * redirection URI, or if the client identifier is missing or invalid,
97
             * the authorization server SHOULD inform the resource owner of the
98
             * error and MUST NOT automatically redirect the user-agent to the
99
             * invalid redirection URI.
100
             */
101
            return new Response(400, ['content-type' => 'application/json'], $e->jsonSerialize());
102
        } catch (InvalidAuthorizationRequest $e) {
103
            /**
104
             * @see https://tools.ietf.org/html/rfc6749#section-4.1.2.1
105
             * If the resource owner denies the access request or if the request
106
             * fails for reasons other than a missing or invalid redirection URI,
107
             * the authorization server informs the client by adding the following
108
             * parameters to the query component of the redirection URI using the
109
             * "application/x-www-form-urlencoded" format, per Appendix B:
110
             *
111
             * error
112
             * REQUIRED.  A single ASCII [USASCII] error code
113
             *
114
             * error_description
115
             * OPTIONAL.  Human-readable ASCII [USASCII] text providing
116
             * additional information, used to assist the client developer in
117
             * understanding the error that occurred.
118
             * Values for the "error_description" parameter MUST NOT include
119
             * characters outside the set %x20-21 / %x23-5B / %x5D-7E.
120
             *
121
             * error_uri
122
             * OPTIONAL.  A URI identifying a human-readable web page with
123
             * information about the error, used to provide the client
124
             * developer with additional information about the error.
125
             * Values for the "error_uri" parameter MUST conform to the
126
             * URI-reference syntax and thus MUST NOT include characters
127
             * outside the set %x21 / %x23-5B / %x5D-7E.
128
             *
129
             * state
130
             * REQUIRED if a "state" parameter was present in the client
131
             * authorization request.  The exact value received from the
132
             * client.
133
             *
134
             */
135
            $oauthException = $e->getOauthException();
136
            $responseData = [
137
                'error' => $oauthException->getError()
138
            ];
139
140
            if ($oauthException->getErrorDescription()) {
141
                $responseData['error_description'] = $oauthException->getErrorDescription();
142
            }
143
144
            if ($oauthException->getErrorUri()) {
145
                $responseData['error_uri'] = $oauthException->getErrorUri();
146
            }
147
148
            if (!empty($e->getState())) {
149
                $responseData['state'] = $e->getState();
150
            }
151
152
            return $e->getResponseMode()->buildResponse($e->getRedirectUri(), $responseData);
153
        }
154
155
        return null;
156
    }
157
158
    /**
159
     * @param ServerRequestInterface $request
160
     * @return ResponseInterface
161
     *
162
     * @see https://tools.ietf.org/html/rfc6749#section-4.1.1
163
     * The client constructs the request URI by adding the following
164
     * parameters to the query component of the authorization endpoint URI
165
     * using the "application/x-www-form-urlencoded" format, per Appendix B:
166
     *
167
     * response_type
168
     * REQUIRED.  Value MUST be set to [desired response type].
169
     *
170
     * client_id
171
     * REQUIRED.  The client identifier as described in Section 2.2.
172
     *
173
     * redirect_uri
174
     * OPTIONAL.  As described in Section 3.1.2.
175
     *
176
     * scope
177
     * OPTIONAL.  The scope of the access request as described by
178
     * Section 3.3.
179
     *
180
     * state
181
     * RECOMMENDED.  An opaque value used by the client to maintain
182
     * state between the request and callback.  The authorization
183
     * server includes this value when redirecting the user-agent back
184
     * to the client.  The parameter SHOULD be used for preventing
185
     * cross-site request forgery as described in Section 10.12.
186
     */
187
    public function handle(ServerRequestInterface $request): ResponseInterface
188
    {
189
        /**
190
         * @see https://tools.ietf.org/html/rfc6749#section-4.1.1
191
         * The authorization server validates the request to ensure that all
192
         * required parameters are present and valid.
193
         */
194
        if ($response = $this->verify($request)) {
195
            return $response;
196
        }
197
198
        if (is_null($this->authorizationRequest)) {
199
            throw new \LogicException();
200
        }
201
202
        try {
203
            /**
204
             * @see https://tools.ietf.org/html/rfc6749#section-4.1.1
205
             * If the request is valid,
206
             * the authorization server authenticates the resource owner and obtains
207
             * an authorization decision (by asking the resource owner or by
208
             * establishing approval via other means).
209
             */
210
            if ($response = $this->verifyConsent($this->authorizationRequest)) {
211
                return $response;
212
            }
213
214
            $responseData = $this->authorizationRequest->getResponseType()
215
                ->handleAuthorizationRequest($this->authorizationRequest);
216
        } catch (OAuthException $e) {
217
            /**
218
             * If the Authorization Server encounters any error, it MUST return an error response, per Section 3.1.2.6.
219
             */
220
            $responseData = [
221
                'error' => $e->getError()
222
            ];
223
            if ($e->getErrorDescription()) {
224
                $responseData['error_description'] = $e->getErrorDescription();
225
            }
226
            if ($e->getErrorUri()) {
227
                $responseData['error_uri'] = $e->getErrorUri();
228
            }
229
        }
230
231
        // todo move state to flows
232
        if (!empty($this->authorizationRequest->getState())) {
233
            $responseData['state'] = $this->authorizationRequest->getState();
234
        }
235
236
        /**
237
         * @see https://tools.ietf.org/html/rfc6749#section-4.1.1
238
         * When a decision is established, the authorization server directs the
239
         * user-agent to the provided client redirection URI using an HTTP
240
         * redirection response, or by other means available to it via the
241
         * user-agent.
242
         */
243
        return $this->authorizationRequest->getResponseMode()
244
            ->buildResponse($this->authorizationRequest->getRedirectUri(), $responseData);
245
    }
246
247
248
    /**
249
     * @param AuthorizationRequest $authorizationRequest
250
     * @return null|ResponseInterface
251
     * @throws OAuthException
252
     */
253
    protected function verifyConsent(AuthorizationRequest $authorizationRequest): ?ResponseInterface
254
    {
255
        $consentGiven = $this->authorizationServerEndUser->hasGivenConsent($authorizationRequest->getClient(), $authorizationRequest->getScopes());
256
        if (is_null($consentGiven)) {
257
            return $this->authorizationServerEndUser->obtainConsent($authorizationRequest);
258
        }
259
260
        if (empty($consentGiven)) {
261
            throw new OAuthException('access_denied', 'The resource owner denied the request.',
262
                'https://tools.ietf.org/html/rfc6749#section-4.1');
263
        }
264
265
        return null;
266
    }
267
268
    protected function verifyResourceOwner(): ?ResponseInterface
269
    {
270
        if (!$this->authorizationServerEndUser->getAuthenticatedResourceOwner()) {
271
            return $this->authorizationServerEndUser->authenticateResourceOwner();
272
        }
273
        return null;
274
    }
275
276
277
    /**
278
     * @return AuthorizationRequest|null
279
     */
280
    public function getAuthorizationRequest(): ?AuthorizationRequest
281
    {
282
        return $this->authorizationRequest;
283
    }
284
285
    /**
286
     * @return EndUserInterface
287
     */
288
    public function getAuthorizationServerEndUser(): EndUserInterface
289
    {
290
        return $this->authorizationServerEndUser;
291
    }
292
}