Commit 93861e09 by Qiang Xue

Refactored auth methods.

parent 74322593
...@@ -140,25 +140,6 @@ class User extends ActiveRecord ...@@ -140,25 +140,6 @@ class User extends ActiveRecord
In the following subsections, we will explain in more details about implementing RESTful APIs. In the following subsections, we will explain in more details about implementing RESTful APIs.
HTTP Status Code Summary
------------------------
* `200`: OK. Everything worked as expected.
* `201`: A resource was successfully created in response to a `POST` request. The `Location` header
contains the URL pointing to the newly created resource.
* `204`: The request is handled successfully and the response contains no body content (like a `DELETE` request).
* `304`: Resource was not modified. You can use the cached version.
* `400`: Bad request. This could be caused by various reasons from the user side, such as invalid JSON
data in the request body, invalid action parameters, etc.
* `401`: No valid API access token is provided.
* `403`: The authenticated user is not allowed to access the specified API endpoint.
* `404`: The requested resource does not exist.
* `405`: Method not allowed. Please check the `Allow` header for allowed HTTP methods.
* `415`: Unsupported media type. The requested content type or version number is invalid.
* `422`: Data validation failed (in response to a `POST` request, for example). Please check the response body for detailed error messages.
* `429`: Too many requests. The request is rejected due to rate limiting.
* `500`: Internal server error. This could be caused by internal program errors.
Data Formatting Data Formatting
--------------- ---------------
...@@ -191,8 +172,30 @@ Caching ...@@ -191,8 +172,30 @@ Caching
Rate Limiting Rate Limiting
------------- -------------
HTTP Status Code Summary
------------------------
* `200`: OK. Everything worked as expected.
* `201`: A resource was successfully created in response to a `POST` request. The `Location` header
contains the URL pointing to the newly created resource.
* `204`: The request is handled successfully and the response contains no body content (like a `DELETE` request).
* `304`: Resource was not modified. You can use the cached version.
* `400`: Bad request. This could be caused by various reasons from the user side, such as invalid JSON
data in the request body, invalid action parameters, etc.
* `401`: No valid API access token is provided.
* `403`: The authenticated user is not allowed to access the specified API endpoint.
* `404`: The requested resource does not exist.
* `405`: Method not allowed. Please check the `Allow` header for allowed HTTP methods.
* `415`: Unsupported media type. The requested content type or version number is invalid.
* `422`: Data validation failed (in response to a `POST` request, for example). Please check the response body for detailed error messages.
* `429`: Too many requests. The request is rejected due to rate limiting.
* `500`: Internal server error. This could be caused by internal program errors.
Documentation Documentation
------------- -------------
Testing Testing
------- -------
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\rest;
use yii\web\User;
use yii\web\Request;
use yii\web\Response;
use yii\web\IdentityInterface;
use yii\web\UnauthorizedHttpException;
/**
* AuthInterface is the interface required by classes than support user authentication.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
interface AuthInterface
{
/**
* Authenticates the current user.
*
* @param User $user
* @param Request $request
* @param Response $response
* @return IdentityInterface the authenticated user identity. If authentication information is not provided, null will be returned.
* @throws UnauthorizedHttpException if authentication information is provided but is invalid.
*/
public function authenticate($user, $request, $response);
/**
* Handles authentication failure.
* The implementation should normally throw UnauthorizedHttpException to indicate authentication failure.
* @param Response $response
* @throws UnauthorizedHttpException
*/
public function handleFailure($response);
}
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace yii\rest; namespace yii\rest;
use Yii; use Yii;
use yii\base\InvalidConfigException;
use yii\web\Response; use yii\web\Response;
use yii\web\UnauthorizedHttpException; use yii\web\UnauthorizedHttpException;
use yii\web\UnsupportedMediaTypeHttpException; use yii\web\UnsupportedMediaTypeHttpException;
...@@ -33,18 +34,6 @@ class Controller extends \yii\web\Controller ...@@ -33,18 +34,6 @@ class Controller extends \yii\web\Controller
* The name of the header parameter representing the API version number. * The name of the header parameter representing the API version number.
*/ */
const HEADER_VERSION = 'version'; const HEADER_VERSION = 'version';
/**
* HTTP Basic authentication.
*/
const AUTH_TYPE_BASIC = 'Basic';
/**
* HTTP Bearer authentication (the token obtained through OAuth2)
*/
const AUTH_TYPE_BEARER = 'Bearer';
/**
* Authentication by an access token passed via a query parameter
*/
const AUTH_TYPE_QUERY = 'Query';
/** /**
* @var string|array the configuration for creating the serializer that formats the response data. * @var string|array the configuration for creating the serializer that formats the response data.
...@@ -55,18 +44,10 @@ class Controller extends \yii\web\Controller ...@@ -55,18 +44,10 @@ class Controller extends \yii\web\Controller
*/ */
public $enableCsrfValidation = false; public $enableCsrfValidation = false;
/** /**
* @var string|array the supported authentication type(s). Valid values include [[AUTH_TYPE_BASIC]], * @var array the supported authentication methods. This property should take a list of supported
* [[AUTH_TYPE_BEARER]] and [[AUTH_TYPE_QUERY]]. * authentication methods, each represented by an authentication class or configuration.
*/
public $authType = [self::AUTH_TYPE_BASIC, self::AUTH_TYPE_BEARER, self::AUTH_TYPE_QUERY];
/**
* @var string the authentication realm to display in case when authentication fails.
*/
public $authRealm = 'api';
/**
* @var string the name of the query parameter containing the access token when [[AUTH_TYPE_QUERY]] is used.
*/ */
public $authParam = 'access-token'; public $authMethods = ['yii\rest\HttpBasicAuth', 'yii\rest\HttpBearerAuth', 'yii\rest\QueryParamAuth'];
/** /**
* @var string the chosen API version number * @var string the chosen API version number
* @see supportedVersions * @see supportedVersions
...@@ -182,36 +163,25 @@ class Controller extends \yii\web\Controller ...@@ -182,36 +163,25 @@ class Controller extends \yii\web\Controller
*/ */
protected function authenticate() protected function authenticate()
{ {
$request = Yii::$app->getRequest(); if (empty($this->authMethods)) {
foreach ((array)$this->authType as $authType) { return;
switch ($authType) {
case self::AUTH_TYPE_BASIC:
$accessToken = $request->getAuthUser();
break;
case self::AUTH_TYPE_BEARER:
$authHeader = $request->getHeaders()->get('Authorization');
if ($authHeader !== null && preg_match("/^{$this->authType}\\s+(.*?)$/", $authHeader, $matches)) {
$accessToken = $matches[1];
}
break;
case self::AUTH_TYPE_QUERY:
$accessToken = $request->get($this->authParam);
break;
}
if (isset($accessToken)) {
break;
}
} }
if (!isset($accessToken) || !Yii::$app->getUser()->loginByAccessToken($accessToken)) { $user = Yii::$app->getUser();
if (!isset($accessToken, $authType)) { $request = Yii::$app->getRequest();
$authType = is_array($this->authType) ? reset($this->authType) : $this->authType; $response = Yii::$app->getResponse();
} foreach ($this->authMethods as $i => $auth) {
if ($authType !== self::AUTH_TYPE_QUERY) { $this->authMethods[$i] = $auth = Yii::createObject($auth);
Yii::$app->getResponse()->getHeaders()->set('WWW-Authenticate', "{$authType} realm=\"{$this->authRealm}\""); if (!$auth instanceof AuthInterface) {
throw new InvalidConfigException(get_class($auth) . ' must implement yii\rest\AuthInterface');
} elseif ($auth->authenticate($user, $request, $response) !== null) {
return;
} }
throw new UnauthorizedHttpException(empty($accessToken) ? 'Access token required.' : 'You are requesting with an invalid access token.');
} }
/** @var AuthInterface $auth */
$auth = reset($this->authMethods);
$auth->handleFailure($response);
} }
/** /**
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\rest;
use Yii;
use yii\base\Component;
use yii\web\UnauthorizedHttpException;
/**
* HttpBasicAuth implements the HTTP Basic authentication method.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class HttpBasicAuth extends Component implements AuthInterface
{
/**
* @var string the HTTP authentication realm
*/
public $realm = 'api';
/**
* @inheritdoc
*/
public function authenticate($user, $request, $response)
{
if (($accessToken = $request->getAuthUser()) !== null) {
$identity = $user->loginByAccessToken($accessToken);
if ($identity !== null) {
return $identity;
}
$this->handleFailure($response);
}
return null;
}
/**
* @inheritdoc
*/
public function handleFailure($response)
{
$response->getHeaders()->set('WWW-Authenticate', "Basic realm=\"{$this->realm}\"");
throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\rest;
use Yii;
use yii\base\Component;
use yii\web\UnauthorizedHttpException;
/**
* HttpBearerAuth implements the authentication method based on HTTP Bearer token.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class HttpBearerAuth extends Component implements AuthInterface
{
/**
* @var string the HTTP authentication realm
*/
public $realm = 'api';
/**
* @inheritdoc
*/
public function authenticate($user, $request, $response)
{
$authHeader = $request->getHeaders()->get('Authorization');
if ($authHeader !== null && preg_match("/^Bearer\\s+(.*?)$/", $authHeader, $matches)) {
$identity = $user->loginByAccessToken($matches[1]);
if ($identity !== null) {
return $identity;
}
$this->handleFailure($response);
}
return null;
}
/**
* @inheritdoc
*/
public function handleFailure($response)
{
$response->getHeaders()->set('WWW-Authenticate', "Basic realm=\"{$this->realm}\"");
throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\rest;
use Yii;
use yii\base\Component;
use yii\web\UnauthorizedHttpException;
/**
* QueryParamAuth implements the authentication method based on the access token passed through a query parameter.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class QueryParamAuth extends Component implements AuthInterface
{
/**
* @var string the parameter name for passing the access token
*/
public $tokenParam = 'access-token';
/**
* @inheritdoc
*/
public function authenticate($user, $request, $response)
{
$accessToken = $request->get($this->tokenParam);
if (is_string($accessToken)) {
$identity = $user->loginByAccessToken($accessToken);
if ($identity !== null) {
return $identity;
}
}
if ($accessToken !== null) {
$this->handleFailure($response);
}
return null;
}
/**
* @inheritdoc
*/
public function handleFailure($response)
{
throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment