There are three kinds of authentication:
HttpBasicAuth
class): This method uses the WWW-Authenticate HTTP header to send the username and password for every requestQueryParamAuth
class): This method uses an access token passed as query parameter in the API URLHttpBearerAuth
class): This method uses an access token that is obtained by the consumer from an authorization server and sent to the API server via HTTP bearer tokensYii supports all the methods mentioned, but we can also easily create a new one.
To enable authentication, follow these steps:
enableSession
to false
in order to make user authentication status not persistent using a session across requests. Next, set loginUrl
to null
to show the HTTP 403 error instead of redirecting it to the login page.authenticator
behavior in API controller classes.yiiwebIdentityInterface::findIdentityByAccessToken()
in the user identity class.Step 1 can be configured in api/config/main.php
:
'components' => [ ... 'user' => [ 'identityClass' => 'commonmodelsUser', 'enableSession' => false, 'loginUrl' => null ], ];
Step 2 requires that we extend the behaviors()
controller method, specifying a single authenticator:
public function behaviors() { $behaviors = parent::behaviors(); $behaviors['authenticator'] = [ 'class' => yiifiltersauthHttpBasicAuth::className(), ]; return $behaviors; }
Or we can do this by specifying multiple authenticators:
public function behaviors() { $behaviors = parent::behaviors(); $behaviors['authenticator'] = [ 'class' => yiifiltersauthCompositeAuth::className(), 'authMethods' => [ yiifiltersauthHttpBasicAuth::className(), yiifiltersauthHttpBearerAuth::className(), yiifiltersauthQueryParamAuth::className(), ], ]; return $behaviors; }
Finally, step 3 requires the implementation of findIdentityByAccessToken()
of the identityClass
specified in the configuration file.
In a simple scenario, the access token can be stored in a column of the User
table and then retrieved:
public static function findIdentityByAccessToken($token, $type = null) { return static::findOne(['access_token' => $token]); }
At the end of the configuration, every request will try to authenticate the user in the beforeAction()
method of the same controller.
Now, let's take a look at the first authentication method, HTTPBasicAuth
. This method requires us to set the auth
property to the callable PHP function; if it is not set, the username will be used as the access token passed to the yiiwebUser::loginByAccessToken()
method.
The basic implementation of the HttpBasicAuth
authentication is:
public function behaviors() { $behaviors = parent::behaviors(); $behaviors['authenticator'] = [ 'class' => yiifiltersauthHttpBasicAuth::className(), 'auth' => function($username, $password) { // return null or identity interface // For example search by username and password return commonmodelsUser::findOne(['username' => $username, 'password' => $password); } /* 'auth' => [$this, 'httpBasicAuthHandler'], */ ]; return $behaviors; } public function httpBasicAuthHandler($username, $password) { // For example search by username and password return commonmodelsUser::findOne(['username' => $username, 'password' => $password]); }
The callable PHP function stored by the auth
property can be represented as an inline function, or as an array, whose first value is the object and the second is the function name to be called, by passing $username
and $password
parameters.
Check how PHP is running through phpinfo()
. If you display CGI/FCGI, then you need to add SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
in .htaccess
to use HTTP Auth from PHP.
The second authentication method is query parameter, by using the QueryParamAuth
class. With this method, a query parameter named access-token
must be passed to the URL. Then, it will call the yiiwebuser::loginByAccessToken()
method, passing access-token
as the first parameter. This function will return an IdentityInterface
or null
.
The URL parameter name can be changed using tokenParam
in the authentication declaration:
public function behaviors() { $behaviors = parent::behaviors(); $behaviors['authenticator'] = [ 'class' => yiifiltersauthQueryParamAuth::className(), 'tokenParam' => 'myAccessToken' ]; return $behaviors; }
With this configuration, the URL must be http://hostname/url?myAccessToken=...
The last authentication method, OAuth 2, requires an authorization server from which we will get the bearer token to pass to the REST API server, which is similar to QueryParamAuth
.
In this example, we are going to authenticate ourselves by using two methods at the same time: HTTPBasicAuth
and QueryParamAuth
. When using QueryParamAuth
with an access token, we will first call a publically accessible action to get an access token that the user will pass to all the other actions as the query URL parameter.
We will start by creating a new model from the Customer
database table and putting it into the common/models
folder. Then, we will create a new user in the User
database table using, for example, foo
as the username and $2a$12$xzGZB29iqBHva4sEYbJeT.pq9g1/VdjoD0S67ciDB30EWSCE18sW6
as the password (this is equivalent to the hashed bar text).
Create a new controller in api/controllers/CustomersController.php
that only extends the behaviors()
method to implement HTTPBasicAuth
and QueryParamAuth
:
<?php namespace apicontrollers; use yii estActiveController; use yiifiltersauthCompositeAuth; use yiifiltersauthHttpBasicAuth; use yiifiltersauthQueryParamAuth; class CustomersController extends ActiveController { public $modelClass = 'commonmodelsCustomer'; public function behaviors() { $behaviors = parent::behaviors(); $behaviors['authenticator'] = [ 'class' => CompositeAuth::className(), 'authMethods' => [ [ 'class' => HttpBasicAuth::className(), 'auth' => function($username, $password) { $out = null; $user = commonmodelsUser::findByUsername($username); if($user!=null) { if($user->validatePassword($password)) $out = $user; } return $out; } ], [ 'class' => QueryParamAuth::className(), ] ] ]; return $behaviors; } }
In HTTPBasicAuth
, we implement the auth
property inside the configuration array by checking $username
and then validating the password. If the username and password match each other, it will return the user found or will otherwise be null.
QueryParamAuth
, instead, does not need any property other than the class, since we will use access-token
as the query parameter name. Nevertheless, to complete this task, we need an action that will return the related user's access token after passing both the username and password.
For this purpose, we will add the actionAccessTokenByUser()
method, which looks for the user with the $username
and $password
parameters passed. If the user already exists, its access_token
property will be updated with a random string, so every time we call this action, access_token
will change and the previous one will be cancelled:
public function actionAccessTokenByUser($username, $passwordHash) { $accessToken = null; $user = commonmodelsUser::findOne(['username' => $username, 'password_hash' => $passwordHash]); if($user!=null) { $user->access_token = Yii::$app->security->generateRandomString(); $user->save(); $accessToken = $user->access_token; } return [ 'access-token' => $accessToken ]; }
Finally, to test HTTPBasicAuth
, we need to pass the WWW-Authentication header by calling the http://hostname/yiiadv/api/web/customers/index
URL.
If we want to use QueryParamAuth
, we need to:
QueryParamAuth
calls the findIdentityByAccessToken()
function of IdentityInterfaces
(the user mode ). So, check that the method is implemented, and if it's not, implement it as follows:
public static function findIdentityByAccessToken($token, $type = null) { return User::findOne(['access_token' => $token]); }
Pay attention, as this way of using access tokens allows the use of the REST API with the same credentials for only one client at a time. This is because any time an access-token-by-user
is called, a new access-token
will be created. Therefore, it should be created a relation one-to-many between users and access-token
in order to provide multiple clients with access using the same username/password credentials.
3.137.187.233