After looking at various questions and issues related to CORS, I still facing the same problem I am not sure what is going wrong with the CORS and authentication. I also tried to post a question on StackOverflow which is unanswered as of now. So this is my problem
I am trying to use ajax request in reactJs to fetch a resource that require authentication from _Bearer Auth in Yii2_.
Request Headers
Accept: /
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
Access-Control-Request-Headers: authorization
Access-Control-Request-Method: GET
Connection: keep-alive
Host: localhost
Origin: http://localhost:8110
Referer: http://localhost:8110/
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Response Headers
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Authorization
Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
Access-Control-Allow-Origin: http://localhost:8110
Connection: Keep-Alive
Content-Length: 150
Content-Type: application/json; charset=UTF-8
Date: Fri, 21 Apr 2017 10:36:27 GMT
Keep-Alive: timeout=5, max=99
Server: Apache/2.4.25 (Ubuntu)
Www-Authenticate: Bearer realm="api"
This is my behavior method in yii2 framework
public function behaviors()
{
$behaviors = parent::behaviors();
unset($behaviors['authenticator']);
return [
[
'class' => 'yii\filters\ContentNegotiator',
'only' => ['view', 'index', 'filters', 'slug', 'shortlist'], // in a controller
// if in a module, use the following IDs for user actions
'formats' => [
'application/json' => Response::FORMAT_JSON,
],
],
'corsFilter' => [
'class' => \yii\filters\Cors::className(),
'cors' => [
'Origin' => ['*'],
'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'OPTIONS'],
'Access-Control-Request-Headers' => ['*'],
'Access-Control-Allow-Credentials' => true,
],
],
'authenticator' => [
'class' => \yii\filters\auth\HttpBearerAuth::className(),
'except' => ['options'],
],
];
}
I know this some issue related to _CORS_ because the same request is working fine with Postman and cURL(from terminal) with the same token, reactjs and ajax showing the same error.
Should not be CORS because you're requesting from the same host.
Thanks for posting in our issue tracker.
In order to properly assist you, we need additional information:
Thanks!
_This is an automated comment, triggered by adding the label status:need more info._
Okay, I have two http servers on my localhost one of them running reactjs on port 8110 and yii2 is on 80. So I am pasting the screenshot of simpler ajax one from port 80 js console only where it works and 8110 console where it shows the above error.


the same request is working fine with Postman and cURL(from terminal) with the same token, reactjs and ajax showing the same error.
What the browser is sending is an OPTIONS request, it is the preflight request it needs to perform before sending the real one (it shouldn't hold any token). Default action to respond to that is defined in ActiveController class if you are extending yii/rest/Controller instead be sure to manually include it. Also be sure routing to it is correctly defined within your UrlManager rules.
/* Rest Urls */
[
'class' => 'yii\rest\UrlRule',
'controller' => ['scholarship'],
'extraPatterns' => [
'OPTIONS index' => 'options',
'POST filters' => 'filters',
'GET {slug}' => 'slug',
],
'tokens' => [
'{slug}' => '<slug>'
],
],
/* Rest Urls Ends */
This is my URL rules for /yii/rest/UrlManager
Moreover I am extending yii/rest/ActiveController for my controller
I guess you'll need to add:
'OPTIONS filters' => 'options',
'OPTIONS {slug}' => 'options',
Also the Url your browser is requesting, shouldn't be /scholarships instead of /scholarship/ ?
also the ID token shouldn't be overridden as it is used by the built-in actions in case you are using them:
'tokens' => [
'{id}' => '<id:\\d[\\d,]*>',
'{slug}' => '<slug>'
],
Updated url rules
/* Rest Urls */
[
'class' => 'yii\rest\UrlRule',
'controller' => ['scholarship'],
'extraPatterns' => [
'OPTIONS index' => 'options',
'OPTIONS filters' => 'options',
'OPTIONS {slug}' => 'options',
'POST filters' => 'filters',
'GET {slug}' => 'slug',
],
'tokens' => [
'{id}' => '<id:\\d[\\d,]*>',
'{slug}' => '<slug>'
],
],
/* Rest Urls Ends */
Still facing the same problem.
Also the Url your browser is requesting, shouldn't be /scholarships instead of /scholarship/ ?
I have overridden the base index method, So I think pluralise property won't work.
one more thing I should have told that If I remove the Bearer auth from the controller and allow it to serve without token then everything works fine.
I have overridden the base index method
Do you mean action index or routing to it ? because extraPatterns won't override, it will just merge them with those. I think pluralizing is expected in this case unless you set $pluralize to false or use $pattern to override them all like it is done in this example and adding 'GET,HEAD index' => 'index' to it. what was the working url you sent within POSTMAN ?
Something like this and defined public function actionIndex() within the controller.
public function actions()
{
$actions = parent::actions();
unset($actions['index']);
return $actions;
}
Here is the screenshot of the postman

Not sure if this can help, If I comment my authenticator and send a request it works, and after that uncomment it again, It doesn't send a OPTIONS request first, and Works as it should be. Also
'except' => ['options'] shows 401 whereas 'except' => ['options', 'index'] works fine (but I am not an authenticated user). I mean Its something like its just behaving differently for OPTIONS request/method.
Oh now I understood. it means it was the rule 'GET {slug}' => 'slug' who takes effect which is pointing you to actionsSlug() and not the index related rules.
Well, this might be weird but slug works with a preceding 's' as shown in the screenshot.

So issue solved?
Nope. It works fine on postman, just gives 401 for OPTIONS if requested from cross domain. Its something like OPTIONS is not considering this statement 'except' => ['options'].
I'm facing the same issue right now, using Yii2 as Rest Server with authentication bearer and Framework7/Dom7 as client for javascript calls ($$.ajax object).
Any working solution will be really appreciated
@theloz
Are you using a basic app or advanced app, I guess they have some bug (Or I made some mistake) in configuration of common and frontend directories.
For basic app it works for me.
In CORS, before real request, a "preflighted" request will be send to server with method 'OPTIONS', about "preflighted" request, please refer to Preflighted requests. There is a simple discussion on what's happened with CORS in stackoverflow Setting Authorization header in XMLHttpRequest changes HTTP verb.
Seems that Yii2 auth filters didn't handle this properly that'll lead to invacation failure. After reading the source code. I write a sample to solve the problem, but not very precise.
<?php
namespace common\modules\OAuth2\filters\auth;
use OAuth2\Request;
use Yii;
class CompositeAuth extends \yii\filters\auth\CompositeAuth
{
/**
* {@inheritdoc}
*/
public function beforeAction($action)
{
if ($this->isPreFligt(Yii::$app->request)) {
return true;
}
return parent::beforeAction($action);
}
protected function isPreFligt($request)
{
return $request->isOptions;
}
}
I don't think it's the best practice to extend CompositeAuth. Origininally, I want to extend HttpBearAuth and HttpBasicAuth, but after looking into the code:
/**
* @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], get_class($this));
if ($identity === null) {
$this->handleFailure($response);
}
return $identity;
}
return null;
}
I don't think it's a good idea to spoil the design with overriden authencticate, since the function return an Identity object or null.But "Authorization" header won't be included in prefligted request, so I can't return the identity.
If you have any idea, please don't hesitate to leave a comment.
I made a PR for this issue.
Basicly, it handles the preflight check inside the yii\filter\Cors class, by rejecting the beforeAction in case of an OPTIONS, and setting the response code to 200. This causes the request to stop at the yii\filter\Cors, and not be rejected in any following Authentication filters. This has also the added benefit of not executing the actual code of the request, what could be dangerous, since you don't have authentication.
Therefore, you don't need the 'except' => ['options'] anymore, but that only worked for yii\rest\ActiveRestController anyway.
I believe this was solved
Yes. Thanks @leandrogehlen