Commit 93861e09 by Qiang Xue

Refactored auth methods.

parent 74322593
......@@ -140,25 +140,6 @@ class User extends ActiveRecord
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
---------------
......@@ -191,8 +172,30 @@ Caching
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
-------------
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 @@
namespace yii\rest;
use Yii;
use yii\base\InvalidConfigException;
use yii\web\Response;
use yii\web\UnauthorizedHttpException;
use yii\web\UnsupportedMediaTypeHttpException;
......@@ -33,18 +34,6 @@ class Controller extends \yii\web\Controller
* The name of the header parameter representing the API version number.
*/
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.
......@@ -55,18 +44,10 @@ class Controller extends \yii\web\Controller
*/
public $enableCsrfValidation = false;
/**
* @var string|array the supported authentication type(s). Valid values include [[AUTH_TYPE_BASIC]],
* [[AUTH_TYPE_BEARER]] and [[AUTH_TYPE_QUERY]].
*/
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.
* @var array the supported authentication methods. This property should take a list of supported
* authentication methods, each represented by an authentication class or configuration.
*/
public $authParam = 'access-token';
public $authMethods = ['yii\rest\HttpBasicAuth', 'yii\rest\HttpBearerAuth', 'yii\rest\QueryParamAuth'];
/**
* @var string the chosen API version number
* @see supportedVersions
......@@ -182,36 +163,25 @@ class Controller extends \yii\web\Controller
*/
protected function authenticate()
{
$request = Yii::$app->getRequest();
foreach ((array)$this->authType as $authType) {
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 (empty($this->authMethods)) {
return;
}
if (!isset($accessToken) || !Yii::$app->getUser()->loginByAccessToken($accessToken)) {
if (!isset($accessToken, $authType)) {
$authType = is_array($this->authType) ? reset($this->authType) : $this->authType;
}
if ($authType !== self::AUTH_TYPE_QUERY) {
Yii::$app->getResponse()->getHeaders()->set('WWW-Authenticate', "{$authType} realm=\"{$this->authRealm}\"");
$user = Yii::$app->getUser();
$request = Yii::$app->getRequest();
$response = Yii::$app->getResponse();
foreach ($this->authMethods as $i => $auth) {
$this->authMethods[$i] = $auth = Yii::createObject($auth);
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