When developing REST API in Yii2, I found some development environments do not populate Authorization header in the request; as a result, I was not able to use HttpBearerAuth because the headers were missing in the request. Note that I still can use QueryParamAuth; although, I insist on using HttpBearerAuth instead of QueryParamAuth.
The issue is caused by CGI/FastCGI mode in Apache. I didn’t want to update server-side configuration; thus, do following changes in .htaccess:
- Update .htaccess
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0 RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php
Above changes will create $_SERVER parameters including REDIRECT_HTTP_AUTHORIZATION and the REDIRECT_HTTP_AUTHORIZATION parameter contains Authorization header value.
But Yii2 HttpBearerAuth does not check $_SERVER[‘REDIRECT_HTTP_AUTHORIZATION’] value. Therefore, above .htaccess still not work for HttpBearerAuth.
To workaround the issue, I override HttpBearerAuth and add the code for checking $_SERVER parameter.
- Create a folder filters/auth
- Create a file HttpBearerAuth.php
<?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace app\filters\auth; /** * HttpBearerAuth is an action filter that supports the authentication method based on HTTP Bearer token. * * You may use HttpBearerAuth by attaching it as a behavior to a controller or module, like the following: * * ```php * public function behaviors() * { * return [ * 'bearerAuth' => [ * 'class' => \yii\filters\auth\HttpBearerAuth::className(), * ], * ]; * } * ``` * * @author Qiang Xue <qiang.xue@gmail.com> * @since 2.0 */ class HttpBearerAuth extends \yii\filters\auth\AuthMethod { /** * @var string the HTTP authentication realm */ public $realm = 'api'; /** * @inheritdoc */ public function authenticate($user, $request, $response) { $authHeader = $request->getHeaders()->get('Authorization'); // Added following lines to support fastcgi issue. // To support this, must update .htaccess as below: // # Authorization Headers // RewriteCond %{HTTP:Authorization} . // RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] if($authHeader == null && isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] != "") { $authHeader = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; } if ($authHeader !== null && preg_match('/^Bearer\s+(.*?)$/', $authHeader, $matches)) { $identity = $user->loginByAccessToken($matches[1], get_class($this)); if ($identity === null) { $this->handleFailure($response); } return $identity; } return null; } /** * @inheritdoc */ public function challenge($response) { $response->getHeaders()->set('WWW-Authenticate', "Bearer realm=\"{$this->realm}\""); } }
- Update controller
<?php namespace app\modules\v1\controllers; use yii\rest\ActiveController; class SampleController extends ActiveController { public $modelClass = 'app\models\Sample'; public function __construct($id, $module, $config = []) { parent::__construct($id, $module, $config); } public function behaviors() { $behaviors = parent::behaviors(); $behaviors['authenticator'] = [ 'class' => yii\filters\auth\CompositeAuth::className(), 'authMethods' => [ app\filters\auth\HttpBearerAuth::className(), ], ]; return $behaviors; } }