i am building an API with Yii2 framework, i need to tell yii some actions act as public action.
i added except in my controller's behaviors function but its not works
public function behaviors() {
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
'except' => ['NotifyOrder'],
];
return $behaviors;
}
public function actionNotifyOrder() {
echo 1;
}
i am always getting following error when i call my /notify-order url
<response><name>Unauthorized</name><message>Your request was made with invalid credentials.</message><code>0</code><status>401</status><type>yii\web\UnauthorizedHttpException</type></response>
according to the docs you need to tell it the action IDs (the dash-separated format used in urls)
you should have
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
'except' => ['notify-order', 'another-action', 'and-so-on'],
];
Related
I've turned the backend into an API. And the API controllers have HttpBasicAuth type authentication.
The problem is that even after authentication in the frontend, whenever a request is made to the API, the authentication window appears.
How can I do so that when the user authenticates in the frontend, is not requested again the username and password of access when a request is made to the API?
Example a controller in API:
class CategoryController extends ActiveController
{
public $modelClass = 'api\models\Category';
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => CompositeAuth::className(),
'authMethods' => [
[
'class' => HttpBasicAuth::className(),
'auth' => function($username, $password) {
$out = null;
$user = \common\models\User::findByUsername($username);
if ($user != null) {
if ($user->validatePassword($password)) $out = $user;
}
return $out;
}
],
],
];
return $behaviors;
}
}
It is called sharing sessions. It also depends on if your tier apps (frontend and api) are both in the same domain. If it is, configure your frontend and api settings (<app>/frontend/config/main.php and <app>/api/config/main.php) as follow:
'components' => [
...
'request' => [
'csrfParam' => '_csrf-shared',
],
...
'user' => [
'identityClass' => 'common\models\User',
'enableAutoLogin' => true,
'identityCookie' => ['name' => '_identity-shared', 'httpOnly' => true],
],
...
'session' => [
'name' => 'advanced-shared',
],
...
It means you save cookies and session with the same name, so that when you login in frontend, and go to backend/api, the backend side fetches same cookies, therefore you'll be detected as authenticated user.
Here one important note, in order to enableAutoLogin work for both tiers, you should set same cookieValidationKey for both main-local.php settings. You can just set them manually, or edit init.php file to generate one cookieValidationKey for all tiers. (Just make sure you know what you're doing).
By the way, I think it's not a good idea to make simultaneous authentication between frontend and api. If it's frontend and backend then it's still bearable, but api interaction is different compared to frontend.
I suggest to use headers like Authorization: Bearer <token>.. You can get more information about it here Yii2 Rest Authentication
Update
I assume this is what you need.. Create a class, i.e. ApiAuth in common/components folder and paste the following code:
<?php
namespace common\components;
use yii\filters\auth\HttpBasicAuth;
class ApiAuth extends HttpBasicAuth
{
/**
* #inheritdoc
*/
public function authenticate($user, $request, $response)
{
if ($user->identity) {
return $user->identity;
}
return parent::authenticate($user, $request, $response);
}
}
This class extends from yii\filters\auth\HttpBasicAuth. Before calling a browser prompt it checks whether user->dentity is populated. If so, no promt is required.
In your Controller behavior replace HttpBasicAuth with ApiAuth class:
use common\components\ApiAuth;
...
'authMethods' => [
[
'class' => ApiAuth::className(),
'auth' => function($username, $password) {
...
As the user is already authenticated, then I just set the "AccessControl" for connected users. If they are not connected they will receive code 403 instead of 401.
This solves the problem:
class CategoryController extends ActiveController
{
public $modelClass = 'api\models\Category';
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['access'] = [
'class' => \yii\filters\AccessControl::className(),
'rules' => [
[
'allow' => true,
'roles' => ['#'],
],
],
];
return $behaviors;
}
}
I have setup my yii2 restful controller but i would like to exempt an action login from httpbearer auth method since no access token is sent during login
This is the way i have setup
class DefaultController extends Controller
{
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => CompositeAuth::className(),
'authMethods' => [
HttpBasicAuth::className(),
HttpBearerAuth::className(),
QueryParamAuth::className(),
],
];
return $behaviors;
}
public function actionLogin(){
//login logic
}
what do i need to add to the $behaviours['authenticator'] to exempt Login action from it
Since this is ActionFilter you can add in the behavior's config:
'except' => ['login']
I have:
RestModule > TargetController extends BaseController
in BaseController:
public function behaviors()
{
$behaviors['myfilter'] = [
'class' => MyFilter::className(),
'only' => ['rest/target/*'],
];
return $behaviors;
}
but my filter working until "only" is not set or if I set TargetController actions names using "except"
Yii2 versin is 2.0.11.2 on php 5.5 debian8
Since version 2.0.9 action IDs can be specified as wildcards, e.g. site/*. yii2-doc
If you want to attach filter with 'only' property and put IDs as wildcard, e.g. target/*, you should attach it as behavior to module class, not controller.
Try this in your RestModule:
RestModule:
public function behaviors()
{
$behaviors['myfilter'] = [
'class' => MyFilter::className(),
'only' => ['target/*'],
];
return $behaviors;
}
I need a way to active rest when some one using ajax X-Requested-With
and deactivate that when doesn't. with this way I can handle search engine
or users without ajax.(improve SEO)
some research: (using behaviors)
public function behaviors()
{
return [
'verbs' => [
'class' => \yii\web\ResponseFilter::className(),
'actions' => [
'something' => [
'format' => Response::FORMAT_JSON,
],
],
],
];
}
check request headers and change response.
or: (edit rest controller)
edit $serializer = 'yii\rest\Serializer'; to $serializer = null;
or (override after action of rest controller)
public function afterAction($action, $result)
{
$result = parent::afterAction($action, $result);
return $this->serializeData($result);
}
and remove serializeData() some how...
but what is the ultimate way?
I think these ways are not fine...
I have a rest full API in Yii2. I have the user model, controller and created a new rest action called login.
How can I set the action login to be executed by the guest users?
class UserController extends \yii\rest\ActiveController{
public function actions() {
$actions = parent::actions();
$actions['login'] = [
'class' => 'app\modules\user\actions\user\LoginUserAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
];
return $actions;
}
public function checkAccess($action, $model = null, $params = array()) {
return true;
}
}
You can set the access control rules on the behaviors function of your controller, instead of on the checkAccess property on the action:
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['access'] = [
'class' => AccessControl::className(),
'except' => ['login'],
'rules' => [
[
'allow' => true,
'actions' => ['foo', 'foo2'],
'roles' => ['admin'],
]
],
];
return $behaviors;
}
In this example, the access control is applied to all actions except 'login'. I left the rules part so you can have an example of how you make customized rules.