Passed
Push — master ( 8278f2...2a258c )
by Alexandre
02:55
created

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