Missing Authroization header in request

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:

  1. 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.

  1. Create a folder filters/auth
  2. 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}\"");
            }
        }
    
  3. 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;
            }
        }