1

I am making angular 2 / Yii2 app

When I call custom controller/action from Postman with bearer header, everything works fine, but when I call it with the same header from angular 2 application (localhost:4200) I get always Unauthorized 401 error. When I do standard actions like from example from guide like:

GET /users: list all users page by page;
POST /users: create a new user;

everything works fine, only when I create custom action, than I get unauthorized.

This is cross domain application, angular is available on web.example.com, yii2 app is on srv.example.com

namespace app\controllers\restapi;

use yii\filters\auth\CompositeAuth;
use yii\filters\auth\HttpBearerAuth;

class SearchOrderController extends \yii\rest\Controller
{
    public $modelClass = 'app\models\Order';
    public function behaviors()
    {
        $behaviors = parent::behaviors();
        $auth = $behaviors['authenticator'];
        unset($behaviors['authenticator']);
        $behaviors['corsFilter'] = [
                'class' => \yii\filters\Cors::className(),
        ];
        $behaviors['authenticator'] = [
                'class' => CompositeAuth::className(),
                'authMethods' => [
                        HttpBearerAuth::className(),
                ],
        ];
        // avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)
        return $behaviors;
    }
    public function actionSearch(){
        \Yii::$app->response->format=\yii\web\Response::FORMAT_JSON;
        return ['index'=>'some text', 'value'=>123];
    }
}

Also my url manager:

        [
            'class' => 'yii\rest\UrlRule',
            'controller' => 'order',
            'pluralize' => false,
            'extraPatterns' => [
                'GET order/<id:\id+>' => 'order/view',
            ]
        ],
        [
            'class' => 'yii\rest\UrlRule',
            'controller' => 'search-order',
            'pluralize' => false,
            'extraPatterns' => [
                'GET search-order/search' => 'search-order/search',
        ]
3
  • are you sure GET and POST are the failing requests? could you please check your browser's network tab to see if it is not an OPTIONS /users the failing one? Commented Mar 17, 2017 at 10:09
  • it is OPTIONS... as message i get is: OPTIONS srv.example.com/restapi/search-order/search 401 (Unauthorized) what could i do with that? Commented Mar 17, 2017 at 10:40
  • Here's official solution for this issue Commented Mar 30, 2018 at 6:13

2 Answers 2

0

I have solved my problem using solution posted here:

Yii2 Rest - Custom action and OPTIONS method

and implementing custom Cors class:

<?php 
namespace app\components;

use Yii;
use yii\filters\Cors;

class CustomCors extends  Cors

{
    public function beforeAction($action)
    {
        parent::beforeAction($action);

        if (Yii::$app->getRequest()->getMethod() === 'OPTIONS') {
            Yii::$app->getResponse()->getHeaders()->set('Allow', 'POST GET PUT');
            Yii::$app->end();
        }

        return true;
    }
}

and changing my controller behaviours accordingly:

    public function behaviors()
    {
        $behaviors = parent::behaviors();

        // remove authentication filter
        $auth = $behaviors['authenticator'];
        unset($behaviors['authenticator']);

        // add CORS filter
        $behaviors['corsFilter'] = [
                'class' =>CustomCors::className()
        ];

        // re-add authentication filter
        $behaviors['authenticator'] = [
                'class' => CompositeAuth::className(),
                'authMethods' => [
                        HttpBearerAuth::className(),
                ],

        ];

        return $behaviors;
   }
Sign up to request clarification or add additional context in comments.

Comments

0

It looks like it is a preflight request issue. I see two things to fix here.

First you need to implement an action to respond to that request as you are extending yii\rest\Controller. Such action is already implemented by Yii but in its child controller yii\rest\ActiveController. So either you use the actions() function to import it like it is done here or you can simply implement it directly as it is done in this other satck overflow post:

Post Request working in Postman but returns Preflight error in Angular 2 app

Then you'll need to define their related rules:

[
    'class' => 'yii\rest\UrlRule',
    'controller' => 'order',
    'pluralize' => false,
    'extraPatterns' => [
        'GET order/<id:\id+>' => 'order/view',
        'OPTIONS order/<id:\id+>' => 'order/options',
    ]
],
[
    'class' => 'yii\rest\UrlRule',
    'controller' => 'search-order',
    'pluralize' => false,
    'extraPatterns' => [
        'GET search-order/search' => 'search-order/search',
        'OPTIONS search-order/search' => 'search-order/options',
]

Next you'll need to let your authentication allowing preflight requests as the browser won't send any token with them. by adding this line as shown in official docs:

// avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)
$behaviors['authenticator']['except'] = ['options'];

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.