Cheetah
AuthorizeController.php
Go to the documentation of this file.
1 <?php
2 
3 namespace OAuth2\Controller;
4 
10 
15 {
16  private $scope;
17  private $state;
18  private $client_id;
19  private $redirect_uri;
20  private $response_type;
21 
22  protected $clientStorage;
23  protected $responseTypes;
24  protected $config;
25  protected $scopeUtil;
26 
42  public function __construct(ClientInterface $clientStorage, array $responseTypes = array(), array $config = array(), ScopeInterface $scopeUtil = null)
43  {
44  $this->clientStorage = $clientStorage;
45  $this->responseTypes = $responseTypes;
46  $this->config = array_merge(array(
47  'allow_implicit' => false,
48  'enforce_state' => true,
49  'require_exact_redirect_uri' => true,
50  'redirect_status_code' => 302,
51  ), $config);
52 
53  if (is_null($scopeUtil)) {
54  $scopeUtil = new Scope();
55  }
56  $this->scopeUtil = $scopeUtil;
57  }
58 
59  public function handleAuthorizeRequest(RequestInterface $request, ResponseInterface $response, $is_authorized, $user_id = null)
60  {
61  if (!is_bool($is_authorized)) {
62  throw new \InvalidArgumentException('Argument "is_authorized" must be a boolean. This method must know if the user has granted access to the client.');
63  }
64 
65  // We repeat this, because we need to re-validate. The request could be POSTed
66  // by a 3rd-party (because we are not internally enforcing NONCEs, etc)
67  if (!$this->validateAuthorizeRequest($request, $response)) {
68  return;
69  }
70 
71  // If no redirect_uri is passed in the request, use client's registered one
72  if (empty($this->redirect_uri)) {
73  $clientData = $this->clientStorage->getClientDetails($this->client_id);
74  $registered_redirect_uri = $clientData['redirect_uri'];
75  }
76 
77  // the user declined access to the client's application
78  if ($is_authorized === false) {
79  $redirect_uri = $this->redirect_uri ?: $registered_redirect_uri;
80  $this->setNotAuthorizedResponse($request, $response, $redirect_uri, $user_id);
81 
82  return;
83  }
84 
85  // build the parameters to set in the redirect URI
86  if (!$params = $this->buildAuthorizeParameters($request, $response, $user_id)) {
87  return;
88  }
89 
90  $authResult = $this->responseTypes[$this->response_type]->getAuthorizeResponse($params, $user_id);
91 
92  list($redirect_uri, $uri_params) = $authResult;
93 
94  if (empty($redirect_uri) && !empty($registered_redirect_uri)) {
95  $redirect_uri = $registered_redirect_uri;
96  }
97 
98  $uri = $this->buildUri($redirect_uri, $uri_params);
99 
100  // return redirect response
101  $response->setRedirect($this->config['redirect_status_code'], $uri);
102  }
103 
104  protected function setNotAuthorizedResponse(RequestInterface $request, ResponseInterface $response, $redirect_uri, $user_id = null)
105  {
106  $error = 'access_denied';
107  $error_message = 'The user denied access to your application';
108  $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $this->state, $error, $error_message);
109  }
110 
111  /*
112  * We have made this protected so this class can be extended to add/modify
113  * these parameters
114  */
115  protected function buildAuthorizeParameters($request, $response, $user_id)
116  {
117  // @TODO: we should be explicit with this in the future
118  $params = array(
119  'scope' => $this->scope,
120  'state' => $this->state,
121  'client_id' => $this->client_id,
122  'redirect_uri' => $this->redirect_uri,
123  'response_type' => $this->response_type,
124  );
125 
126  return $params;
127  }
128 
129  public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response)
130  {
131  // Make sure a valid client id was supplied (we can not redirect because we were unable to verify the URI)
132  if (!$client_id = $request->query('client_id', $request->request('client_id'))) {
133  // We don't have a good URI to use
134  $response->setError(400, 'invalid_client', "No client id supplied");
135 
136  return false;
137  }
138 
139  // Get client details
140  if (!$clientData = $this->clientStorage->getClientDetails($client_id)) {
141  $response->setError(400, 'invalid_client', 'The client id supplied is invalid');
142 
143  return false;
144  }
145 
146  $registered_redirect_uri = isset($clientData['redirect_uri']) ? $clientData['redirect_uri'] : '';
147 
148  // Make sure a valid redirect_uri was supplied. If specified, it must match the clientData URI.
149  // @see http://tools.ietf.org/html/rfc6749#section-3.1.2
150  // @see http://tools.ietf.org/html/rfc6749#section-4.1.2.1
151  // @see http://tools.ietf.org/html/rfc6749#section-4.2.2.1
152  if ($supplied_redirect_uri = $request->query('redirect_uri', $request->request('redirect_uri'))) {
153  // validate there is no fragment supplied
154  $parts = parse_url($supplied_redirect_uri);
155  if (isset($parts['fragment']) && $parts['fragment']) {
156  $response->setError(400, 'invalid_uri', 'The redirect URI must not contain a fragment');
157 
158  return false;
159  }
160 
161  // validate against the registered redirect uri(s) if available
162  if ($registered_redirect_uri && !$this->validateRedirectUri($supplied_redirect_uri, $registered_redirect_uri)) {
163  $response->setError(400, 'redirect_uri_mismatch', 'The redirect URI provided is missing or does not match', '#section-3.1.2');
164 
165  return false;
166  }
167  $redirect_uri = $supplied_redirect_uri;
168  } else {
169  // use the registered redirect_uri if none has been supplied, if possible
170  if (!$registered_redirect_uri) {
171  $response->setError(400, 'invalid_uri', 'No redirect URI was supplied or stored');
172 
173  return false;
174  }
175 
176  if (count(explode(' ', $registered_redirect_uri)) > 1) {
177  $response->setError(400, 'invalid_uri', 'A redirect URI must be supplied when multiple redirect URIs are registered', '#section-3.1.2.3');
178 
179  return false;
180  }
181  $redirect_uri = $registered_redirect_uri;
182  }
183 
184  // Select the redirect URI
185  $response_type = $request->query('response_type', $request->request('response_type'));
186 
187  // for multiple-valued response types - make them alphabetical
188  if (false !== strpos($response_type, ' ')) {
189  $types = explode(' ', $response_type);
190  sort($types);
191  $response_type = ltrim(implode(' ', $types));
192  }
193 
194  $state = $request->query('state', $request->request('state'));
195 
196  // type and client_id are required
197  if (!$response_type || !in_array($response_type, $this->getValidResponseTypes())) {
198  $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_request', 'Invalid or missing response type', null);
199 
200  return false;
201  }
202 
203  if ($response_type == self::RESPONSE_TYPE_AUTHORIZATION_CODE) {
204  if (!isset($this->responseTypes['code'])) {
205  $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unsupported_response_type', 'authorization code grant type not supported', null);
206 
207  return false;
208  }
209  if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'authorization_code')) {
210  $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null);
211 
212  return false;
213  }
214  if ($this->responseTypes['code']->enforceRedirect() && !$redirect_uri) {
215  $response->setError(400, 'redirect_uri_mismatch', 'The redirect URI is mandatory and was not supplied');
216 
217  return false;
218  }
219  } else {
220  if (!$this->config['allow_implicit']) {
221  $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unsupported_response_type', 'implicit grant type not supported', null);
222 
223  return false;
224  }
225  if (!$this->clientStorage->checkRestrictedGrantType($client_id, 'implicit')) {
226  $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'unauthorized_client', 'The grant type is unauthorized for this client_id', null);
227 
228  return false;
229  }
230  }
231 
232  // validate requested scope if it exists
233  $requestedScope = $this->scopeUtil->getScopeFromRequest($request);
234 
235  if ($requestedScope) {
236  // restrict scope by client specific scope if applicable,
237  // otherwise verify the scope exists
238  $clientScope = $this->clientStorage->getClientScope($client_id);
239  if ((is_null($clientScope) && !$this->scopeUtil->scopeExists($requestedScope))
240  || ($clientScope && !$this->scopeUtil->checkScope($requestedScope, $clientScope))) {
241  $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_scope', 'An unsupported scope was requested', null);
242 
243  return false;
244  }
245  } else {
246  // use a globally-defined default scope
247  $defaultScope = $this->scopeUtil->getDefaultScope($client_id);
248 
249  if (false === $defaultScope) {
250  $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $state, 'invalid_client', 'This application requires you specify a scope parameter', null);
251 
252  return false;
253  }
254 
255  $requestedScope = $defaultScope;
256  }
257 
258  // Validate state parameter exists (if configured to enforce this)
259  if ($this->config['enforce_state'] && !$state) {
260  $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, null, 'invalid_request', 'The state parameter is required');
261 
262  return false;
263  }
264 
265  // save the input data and return true
266  $this->scope = $requestedScope;
267  $this->state = $state;
268  $this->client_id = $client_id;
269  // Only save the SUPPLIED redirect URI (@see http://tools.ietf.org/html/rfc6749#section-4.1.3)
270  $this->redirect_uri = $supplied_redirect_uri;
271  $this->response_type = $response_type;
272 
273  return true;
274  }
275 
287  private function buildUri($uri, $params)
288  {
289  $parse_url = parse_url($uri);
290 
291  // Add our params to the parsed uri
292  foreach ($params as $k => $v) {
293  if (isset($parse_url[$k])) {
294  $parse_url[$k] .= "&" . http_build_query($v, '', '&');
295  } else {
296  $parse_url[$k] = http_build_query($v, '', '&');
297  }
298  }
299 
300  // Put humpty dumpty back together
301  return
302  ((isset($parse_url["scheme"])) ? $parse_url["scheme"] . "://" : "")
303  . ((isset($parse_url["user"])) ? $parse_url["user"]
304  . ((isset($parse_url["pass"])) ? ":" . $parse_url["pass"] : "") . "@" : "")
305  . ((isset($parse_url["host"])) ? $parse_url["host"] : "")
306  . ((isset($parse_url["port"])) ? ":" . $parse_url["port"] : "")
307  . ((isset($parse_url["path"])) ? $parse_url["path"] : "")
308  . ((isset($parse_url["query"]) && !empty($parse_url['query'])) ? "?" . $parse_url["query"] : "")
309  . ((isset($parse_url["fragment"])) ? "#" . $parse_url["fragment"] : "")
310  ;
311  }
312 
313  protected function getValidResponseTypes()
314  {
315  return array(
316  self::RESPONSE_TYPE_ACCESS_TOKEN,
317  self::RESPONSE_TYPE_AUTHORIZATION_CODE,
318  );
319  }
320 
330  protected function validateRedirectUri($inputUri, $registeredUriString)
331  {
332  if (!$inputUri || !$registeredUriString) {
333  return false; // if either one is missing, assume INVALID
334  }
335 
336  $registered_uris = preg_split('/\s+/', $registeredUriString);
337  foreach ($registered_uris as $registered_uri) {
338  if ($this->config['require_exact_redirect_uri']) {
339  // the input uri is validated against the registered uri using exact match
340  if (strcmp($inputUri, $registered_uri) === 0) {
341  return true;
342  }
343  } else {
344  // the input uri is validated against the registered uri using case-insensitive match of the initial string
345  // i.e. additional query parameters may be applied
346  if (strcasecmp(substr($inputUri, 0, strlen($registered_uri)), $registered_uri) === 0) {
347  return true;
348  }
349  }
350  }
351 
352  return false;
353  }
354 
359  public function getScope()
360  {
361  return $this->scope;
362  }
363 
364  public function getState()
365  {
366  return $this->state;
367  }
368 
369  public function getClientId()
370  {
371  return $this->client_id;
372  }
373 
374  public function getRedirectUri()
375  {
376  return $this->redirect_uri;
377  }
378 
379  public function getResponseType()
380  {
381  return $this->response_type;
382  }
383 }
OAuth2\Controller\AuthorizeController\getRedirectUri
getRedirectUri()
Definition: AuthorizeController.php:374
OAuth2\ScopeInterface
Definition: ScopeInterface.php:13
OAuth2\RequestInterface\request
request($name, $default=null)
OAuth2\Controller\AuthorizeController\setNotAuthorizedResponse
setNotAuthorizedResponse(RequestInterface $request, ResponseInterface $response, $redirect_uri, $user_id=null)
Definition: AuthorizeController.php:104
use
GNU LESSER GENERAL PUBLIC LICENSE February Free Software Inc Franklin Fifth MA USA Everyone is permitted to copy and distribute verbatim copies of this license but changing it is not allowed[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it By the GNU General Public Licenses are intended to guarantee your freedom to share and change free software to make sure the software is free for all its users This the Lesser General Public applies to some specially designated software packages typically libraries of the Free Software Foundation and other authors who decide to use it You can use it but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular based on the explanations below When we speak of free we are referring to freedom of use
Definition: license.txt:27
OAuth2\Controller\AuthorizeController\getClientId
getClientId()
Definition: AuthorizeController.php:369
OAuth2\Scope
Definition: Scope.php:12
php
OAuth2\Controller\AuthorizeController\$responseTypes
$responseTypes
Definition: AuthorizeController.php:23
OAuth2\Storage\ClientInterface
Definition: ClientInterface.php:12
OAuth2\ResponseInterface\setError
setError($statusCode, $name, $description=null, $uri=null)
OAuth2\Controller\AuthorizeController\$clientStorage
$clientStorage
Definition: AuthorizeController.php:22
OAuth2\Controller\AuthorizeController\getState
getState()
Definition: AuthorizeController.php:364
OAuth2\Controller\AuthorizeController\validateAuthorizeRequest
validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response)
Definition: AuthorizeController.php:129
OAuth2\Controller\AuthorizeController\__construct
__construct(ClientInterface $clientStorage, array $responseTypes=array(), array $config=array(), ScopeInterface $scopeUtil=null)
Definition: AuthorizeController.php:42
OAuth2\Controller\AuthorizeController\getResponseType
getResponseType()
Definition: AuthorizeController.php:379
OAuth2\Controller
Definition: AuthorizeController.php:3
OAuth2\ResponseInterface
Definition: ResponseInterface.php:12
OAuth2\Controller\AuthorizeControllerInterface
Definition: AuthorizeControllerInterface.php:27
OAuth2\Controller\AuthorizeController
Definition: AuthorizeController.php:15
OAuth2\RequestInterface
Definition: RequestInterface.php:6
OAuth2\Controller\AuthorizeController\getScope
getScope()
Definition: AuthorizeController.php:359
OAuth2\ResponseInterface\setRedirect
setRedirect($statusCode, $url, $state=null, $error=null, $errorDescription=null, $errorUri=null)
OAuth2\RequestInterface\query
query($name, $default=null)
OAuth2\Controller\AuthorizeController\$scopeUtil
$scopeUtil
Definition: AuthorizeController.php:25
OAuth2\Controller\AuthorizeController\getValidResponseTypes
getValidResponseTypes()
Definition: AuthorizeController.php:313
empty
Attr AllowedRel this is empty
Definition: Attr.AllowedRel.txt:7
OAuth2\Controller\AuthorizeController\buildAuthorizeParameters
buildAuthorizeParameters($request, $response, $user_id)
Definition: AuthorizeController.php:115
as
as
Definition: Filter.ExtractStyleBlocks.Escaping.txt:10
OAuth2\Controller\AuthorizeController\validateRedirectUri
validateRedirectUri($inputUri, $registeredUriString)
Definition: AuthorizeController.php:330
OAuth2\Controller\AuthorizeController\$config
$config
Definition: AuthorizeController.php:24
OAuth2\Controller\AuthorizeController\handleAuthorizeRequest
handleAuthorizeRequest(RequestInterface $request, ResponseInterface $response, $is_authorized, $user_id=null)
Definition: AuthorizeController.php:59