AuthorizationEndpoint::handle()   B
last analyzed

Complexity

Conditions 8
Paths 21

Size

Total Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

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