AngularJS and Yii2 Part 2: Authentication

Our app comes to life.


In this part we will cover form submission, form validation (with help from Yii2) and authentication. Here’s the the demo. The username is demo and the password is demo. Download the source code from GitHub.

Configuring Yii2

If you haven’t configured the database connection in common/config/main-local.php, this would be a good time. After that apply the migration with the php yii migrate command. You can add a demo user with this SQL query if you need to.

INSERT INTO `user` (`id`, `username`, `auth_key`, `password_hash`, `password_reset_token`, `email`, `status`, `created_at`, `updated_at`) VALUES
(1, 'demo', 'u4qnlunMrSWqcyitTV06gH5C8ZlAaWar', '$2y$13$dN9ipH0Pc2zLBsDGfIkLOuZDvG0Lv5YACMWCAUIYeCHqNKfw3VbDa', NULL, '', 10, 1428424049, 1428424049);

To make authentication work we need to implement the findIdentityByAccessToken() function if common/models/User.php. To make this tutorial simple we will use the auth_key field from the user table, provided by Yii2, as the acces token for our user.

public static function findIdentityByAccessToken($token, $type = null)
    return static::findOne(['auth_key' => $token]);

Now let’s configure several components in frontend/config/main.php. We won’t be using cookies for authentication, so we will disable ‘enableSession’ => false, for the ‘user’ component. We also wan’t to receive a 401 response instead of a redirect to the login action so our Angular app would know that access was denied ‘loginUrl’ => null.

'user' => [
    'identityClass' => 'common\models\User',
    'enableSession' => false,
    'loginUrl' => null,

We will also configure a JsonParser for the Request component, because we will be receiving form data as JSON.

'request' => [
    'class' => '\yii\web\Request',
    'enableCookieValidation' => false,
    'parsers' => [
        'application/json' => 'yii\web\JsonParser',

Lastly we want pretty URLs (e.g. /api/dashboard).

'urlManager' => [
    'enablePrettyUrl' => true,
    'showScriptName' => false,


Our frontend/controllers/ApiController.php will extend from yii\rest\Controller instead of yii\web\Controller.

We will be using HTTP Bearer Authentication for our app. AngularjS will send our login and password to Yii2 and Yii2 will send back an access_token. For all the requests after that we will send the access_token as part of the HTTP header in such format:

Authorization: Bearer u4qnlunMrSWqcyitTV06gH5C8ZlAaWar.


To configure the HTTP Bearer Auth method we will make some additions to the behaviors() method of our controller. We only have one action (dashboard) that needs authentication, which is why we will apply the ‘authenticator’ filter only to it.

$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
    'class' => HttpBearerAuth::className(),
    'only' => ['dashboard'],

Note that we want to keep the behaviors() configuration from yii\rest\Controller which is why we preserve it in the first line $behaviors = parent::behaviors(); and keep adding to it.

Yii2 is capable of XML and JSON responses for REST APIs, but we will only use JSON this time.

$behaviors['contentNegotiator'] = [
    'class' => ContentNegotiator::className(),
    'formats' => [
        'application/json' => Response::FORMAT_JSON,

We also wan’t to allow access to the dashboard only for authenticated users.

$behaviors['access'] = [
    'class' => AccessControl::className(),
    'only' => ['dashboard'],
    'rules' => [
            'actions' => ['dashboard'],
            'allow' => true,
            'roles' => ['@'],

Finally we can return our behavior configuration return $behaviors;


Our login action will look very similar to a standard login action. We will populate the LoginForm model with data from Yii::$app->getRequest()->getBodyParams(). And we also have to validate the form before returning it in case the user submitted an empty form. If authentication was successfull we will send the access_token return [‘access_token’ => Yii::$app->user->identity->getAuthKey()];. This is the final result.

public function actionLogin()
    $model = new LoginForm();

    if ($model->load(Yii::$app->getRequest()->getBodyParams(), '') && $model->login()) {
        return ['access_token' => Yii::$app->user->identity->getAuthKey()];
    } else {
        return $model;

Our dashboard is protected by AccessControl so we only need to provide the data for the view in our dashboard action.

public function actionDashboard()
    $response = [
        'username' => Yii::$app->user->identity->username,
        'access_token' => Yii::$app->user->identity->getAuthKey(),

    return $response;

To make the Flash in the contact view we will send the content and the class of the flash. Here is our contact action.

public function actionContact()
    $model = new ContactForm();
    if ($model->load(Yii::$app->getRequest()->getBodyParams(), '') && $model->validate()) {
        if ($model->sendEmail(Yii::$app->params['adminEmail'])) {
            $response = [
                'flash' => [
                    'class' => 'success',
                    'message' => 'Thank you for contacting us. We will respond to you as soon as possible.',
        } else {
            $response = [
                'flash' => [
                    'class' => 'error',
                    'message' => 'There was an error sending email.',
        return $response;
    } else {
        return $model;

That’s it for our API controller.



We will make a module that will contain our controllers. Let’s tell Angular about it.

var app = angular.module('app', [
    'ngRoute',          //$routeProvider
    'mgcrea.ngStrap',   //bs-navbar, data-match-route directives
    'controllers'       //Our module frontend/web/js/controllers.js

We need to tell the app which view corresponds to which controller.

app.config(['$routeProvider', '$httpProvider',
    function($routeProvider, $httpProvider) {
            when('/', {
                templateUrl: 'partials/index.html',
            when('/about', {
                templateUrl: 'partials/about.html'
            when('/contact', {
                templateUrl: 'partials/contact.html',
                controller: 'ContactController'
            when('/login', {
                templateUrl: 'partials/login.html',
                controller: 'LoginController'
            when('/dashboard', {
                templateUrl: 'partials/dashboard.html',
                controller: 'DashboardController'
                templateUrl: 'partials/404.html'

We also pushed an interceptor called authInterceptor. It will add the access_token to the users requests if the user is logged in. And redirect to the login form in case of a “401 Unauthorized” HTTP status.

app.factory('authInterceptor', function ($q, $window, $location) {
    return {
        request: function (config) {
            if ($window.sessionStorage.access_token) {
                config.headers.Authorization = 'Bearer ' + $window.sessionStorage.access_token;
            return config;
        responseError: function (rejection) {
            if (rejection.status === 401) {
            return $q.reject(rejection);


Our controllers module will have a MainController that will have two functions. One will return true or false, depending of whether the user is logged in or not. We will use it to show and hide certain menu items the same way we use Yii::$app->user->isGuest in Yii.

The other function will handle the ng-click event for the Logout menu item.

var controllers = angular.module('controllers', []);

controllers.controller('MainController', ['$scope', '$location', '$window',
    function ($scope, $location, $window) {
        $scope.loggedIn = function() {
            return Boolean($window.sessionStorage.access_token);

        $scope.logout = function () {
            delete $window.sessionStorage.access_token;

The DashboardController will be very simple. It will request data from ‘api/dashboard’ and push the data into the view.

controllers.controller('DashboardController', ['$scope', '$http',
    function ($scope, $http) {
        $http.get('api/dashboard').success(function (data) {
           $scope.dashboard = data;

The LoginController will have one function login(), that will handle the ng-submit event for the login form. The function makes a POST request to ‘api/login’ and sends the username and password. If the request is successful the received session_token is stored and the user is redirected to the dashboard. In case there is an error (the form data is invalid, or the user doesn’t exist) the error data is pushed into the view, where it will be displayed to the user. Here’s how the error data looks when an empty form is submitted.

[{"field":"username","message":"Username cannot be blank."},{"field":"password","message":"Password cannot be blank."}]

Here’s the code for the LoginController.

controllers.controller('LoginController', ['$scope', '$http', '$window', '$location',
    function($scope, $http, $window, $location) {
        $scope.login = function () {
            $scope.submitted = true;
            $scope.error = {};
            $'api/login', $scope.userModel).success(
                function (data) {
                    $window.sessionStorage.access_token = data.access_token;
                function (data) {
                    angular.forEach(data, function (error) {
                        $scope.error[error.field] = error.message;

The ContactController will be the biggest one in our module. It will have two functions. refreshCaptcha() will handle the ng-click event for the captcha image. It will make a GET request to ‘site/captcha?refresh=1’ to get a different captcha if the currently provided one is not readable. The other function contact() will handle the ng-submit event for the contact form. It will POST the form data to ‘api/contact’ and in case of success it will push the “flash” data to the view. After that the form will be cleared and the captcha will be refreshed. In case of an error it will push the error data to the view.


In order for our MainController to work we need to add a directive to the body tag in our layout in frontend/views/layout/main.php. We will also add new menu items to the navbar.


<ul class="navbar-nav navbar-right nav">
    <li data-match-route="/$">
        <a href="#/">Home</a>
    <li data-match-route="/about">
        <a href="#/about">About</a>
    <li data-match-route="/contact">
        <a href="#/contact">Contact</a>
    <li data-match-route="/dashboard" ng-show="loggedIn()" class="ng-hide">
        <a href="#/dashboard">Dashboard</a>
    <li ng-class="{active:isActive('/logout')}" ng-show="loggedIn()" ng-click="logout()"  class="ng-hide">
        <a href="">Logout</a>
    <li data-match-route="/login" ng-hide="loggedIn()">
        <a href="#/login">Login</a>

Notice the ng-show and ng-hide directives for the last three menu items. They will show or hide menu items depending on the value of the loggedIn() function.

Login form

We need to add several directives to frontend/web/partials/login.html to make the form work. ng-submit=”login()” will tell AngularJS to run the login() function when the form is submitted.

The ng-class directive will help us to set a ‘has-success’ or ‘has-errors’ css class for form inputs depending on whether they have validation errors or not.


ng-model=”userModel.username”   binds input field data to a variable.

Finally we will display error messages in the following way

<p class="help-block help-block-error">{{ error['username'] }}</p>

Here’s how our form will look.

<div class="row">
    <div class="col-lg-5">
        <form ng-submit="login()" name="loginForm" id="login-form" method="post" role="form" >
            <div ng-class="{ 'has-success': !error['username'] && submitted,
                'has-error': error['username'] && submitted }"
                 class="form-group field-loginform-username required">
                <label class="control-label" for="loginform-username">Username</label>
                <input ng-model="userModel.username" type="text" id="loginform-username" class="form-control">
                <p class="help-block help-block-error">{{ error['username'] }}</p>

            <div ng-class="{ 'has-success': !error['password'] && submitted,
                'has-error': error['password'] && submitted }"
                 class="form-group field-loginform-password required">
                <label class="control-label" for="loginform-password">Password</label>
                <input ng-model="userModel.password" type="password" id="loginform-password" class="form-control">
                <p class="help-block help-block-error">{{ error['password'] }}</p>

            <div class="form-group">
                <button type="submit" class="btn btn-primary" name="login-button">Login</button>


 Contact form

The contact form will be very similar. Except for two parts.

In the beginning we will include a div for the Flash message.


It will normally be hidden thanks to the ng-show directive and will only appear when the flash variable is pushed to the scope of the view.

<div ng-show="flash" class="alert alert-{{ flash.class }}">

The other part that’s different about the contact form is the captcha image. It has a ng-click event and the source for the image is provided by the captchUrl variable.

<img ng-click="refreshCaptcha()" ng-src="{{captchaUrl}}" id="contactform-verifycode-image" alt="">


Let’s add our controllers.js to frontend/assets/AppAsset.php so Yii2 would add it to our layout.

public $js = [


Try adding the signup form yourself. It’s very similar to the login form.

  1. Add a signup action to frontend/controllers/ApiController.php
  2. Add a signup controller to frontend/web/js/controllers.js
  3. Add a signup menu item to frontend/views/layouts/main.php
  4. Add a partial view with the signup form to frontend/web/partials/


Yii2 is great for building RESTful services. It provides three authentication methods HTTP Basic Auth, HTTP Bearer Auth and Query Parameter Auth. They can be used separately or together thanks to  yii\filters\auth\CompositeAuth.

The form validation method implemented in this tutorial is incomplete. It can and should be improved for production grade applications, which is beyond the scope of this tutorial. The AngularJS part of the application can benefit from restructuring. If you wan’t to learn more about building Angular apps you can examine the code of angular-app. Angular apps that have node.js back ends benefit greatly from code sharing (e.g. validation rules), which isn’t possible for PHP back ends.

Yii2 also provides yii\rest\ActiveController for RESTful services, which would be a great topic for a third part.

Published by


Your friendly, neighborhood, full stack, pseudorandom text generator

64 thoughts on “AngularJS and Yii2 Part 2: Authentication”

  1. I am getting bellow error: {“name”:”PHP Fatal Error”,”message”:”Class ‘frontend\\controllers\\Yii’ not found”,”code”:1,”type”:”yii\\base\\ErrorException”,”file”:”C:\\xampp\\htdocs\\yii2\\angular-tute\\frontend\\controllers\\ApiController.php”,”line”:71,”stack-trace”:[“#0 [internal function]: yii\\base\\ErrorHandler->handleFatalError()”,”#1 {main}”]}
    $model->load(Yii::$app->getRequest()->getBodyParams(), ”) is on line 71.

  2. Hi Alex, I am getting 404 error when clicking the Login button, which sent the POST to /api/login.
    You didn’t seem to make any routing configuration, how can yii match the request to /api/login?



    1. Hello, James.
      I uploaded the source code of my demo to GitHub. If you’ve downloaded that code and it produces the 404 message – please provide more details, I’m interested in finding out why that is happening.
      If you were following the instructions in the tutorial – try comparing your code to my code on GitHub.

      When no routing information is provided Yii2 assumes that all routes follow the controller/action or module/controller/action convention. That is why the route /api/login gets handled by ApiControllers loginAction without any routing rules.

      If you want more control over the routes by all means define custom rules and enable strict parsing.

      Good luck!

      1. I have also clone your project from git and then run the composer update, php init and ./yii migrate commands. Could not do login with demo account . Getting 404 for every other request.

        1. You have to run composer install instead of composer update, this ensures that you will get the same versions of all dependencies. Also you shouldn’t run migrations becuse in this example I used SQLite and the database is provided with the code so it should work outside the box.
          I might be able to help you if you are willing to follow the steps I mentioned above and provide more details.

          1. Hi Alex,

            Are you still active on this blog. I came back to this blog after 5 months. I tried the step you mentioned, I am still not able to login using demo/demo. the control goes the the login function in the controller,but fail at the post request to ap/login, and return 404 error message

          2. Hi yoges,

            I can’t say I’m active on this blog. Not enough free time to write.
            It’s difficult to debug without any data.
            It might be related to the web server configuration.
            Might be related to the setup.
            Might be related to my code.
            I can’t tell just from your comment.

            Best regards,

        2. [Solution]:

          1.- Check .htaccess in the web folder exists.

          2. in apache2 active mod_rewrite and in sites-available The AllowOverride directive was set to None, which disabled URL rewrites. I changed it to All and now pretty URLs work nicely.

          Options Indexes
          FollowSymLinks MultiViews
          AllowOverride All
          Require all granted

          1. This was a big help. Thanks!

            All I really needed was:

            1) change “AllowOverride None” to “AllowOverride Options FileInfo” at the “/var/www/html” level (YMMV)

            2) Add “Options +FollowSymLinks” at the top of my .htaccess in the main web folder. Had to add .htaccess itself, because I started from scratch.

            Great tutorial. Thanks!

  3. great tutorials! but are’nt we losing the Urlmanager functionality by changing all the schema to angular?

    1. Thank you. Yes, we aren’t using a lot of the functionality that Yii provides but that’s ok. Angular takes care of the routing, while Yii provides an API for Angular. You can combine Angular and Yii in many ways. This is just one example.

    1. Unless you are planing to generate the partial views with Yii – no. Otherwise it’s entirely possible, but I wouldn’t suggest this. You can serve all the necessary data as JSON and then render it with Angulars help in a partial view.

  4. I clone your code from github, but I’m getting this error: “ReferenceError: angular is not defined”

    in web assets angular folder is missing.
    how I can fix this issue?

    1. Interesting. If Angular is missing from the web/assets folder then yii is not publishing assets correctly. Is there anything at all in the web/assets folder?
      Maybe it’s not writable?

  5. It is really very helpful for me, thank you.
    But i can’t used this example for CORS.
    All times i have 422 error on OPTION request.

    In Api controller i tried this:
    $behaviors[‘corsFilter’] = [
    ‘class’ => \yii\filters\Cors::className(),
    ‘cors’ => [
    ‘Origin’ => [‘*’],
    ‘Access-Control-Request-Methods’ => [‘GET’, ‘POST’, ‘OPTIONS’, ‘DELETE’, ‘PUT’],
    Any ideas?

    1. You are not the only one who is having this problem.
      This is happening because the browser is sending a Preflight Request.
      You can read about it here
      The preflight request checks for allow headers and doesn’t contain any data. No login credentials. No auth token. This confuses the Authenticator.
      This isssue has been known for a while, but there is no fix available yet.

  6. I keep gettting window.sessionStorage undefined:

    Error: $window.sessionStorage is undefined

    Any help for me?

    1. Actually, this is line:10:3
      $scope.loggedIn = Boolean($window.sessionStorage.access_token);
      Just same as above.
      I tried using the localStorage just for testing. It also doesn’t work with likely error msg.

      1. Today I cloned the repository from github and it’s working properly for me. Did you use the code from github or did you follow the instructions?

        In the code that I provided that line is eight, not tenth. Did you change anything?
        At the moment I can only suggest comparing your code to the code on github.

  7. Hi, nice tutorial I have gone through your tutorial and created a module which contains models folder with User.php class in it. This user model is special to this module( am not using the one in common as in your case). But when I try return [‘access_token’ => Yii::$app->user->identity->getAuthKey()]; in my login I get null. Also my HttpBearerAuth::ClassName keeps on returning Unauthorized 401 exception. To my guess is that my User class in the module is not configured correctly to return the data it need in this module. Also I have overriden the database in this module user model and everything is working fine. Please help. if you need to look I can send you through email. Just let me know via email.

    1. Great question Bonnie.

      Yii probably doesn’t know about your User class. You can tell Yii about it by configuring the user component. If used your approach my config would probably have something like this:

      'user' => [
      'identityClass' => 'frontend\modules\mymodule\models\User',
      'enableSession' => false,
      'loginUrl' => null,

      Tell me if that helps.

      1. Thanks for quick answer.
        I have that in the config of my module not in the common folder
        ‘user’ => [
        ‘identityClass’ => ‘api\modules\v1\models\User’,
        ‘enableAutoLogin’ => false,
        ‘enableSession’ => false,
        ‘loginUrl’ => null,
        And this is my folder structure but my main concern is in api folder
        -main.php = contains the user component above for this module, it is not in the common folder to the application
        -User.php the user class am using not the one in common

        1. If your user component is not configured in common, then you have to access it differently.

          If the user component is configured in the module and module is called v1 try something like this:

          Good luck!

          1. Am getting below error
            “name”: “Unknown Property”,
            “message”: “Getting unknown property: api\\modules\\v1\\Module::user”,

            I don’t know if I need to create a component folder in my module then create a User component that extends the \yii\web\User.
            Also I have been unluck to get HttpBearerAuth working in my conroller and my thinking is because of the user component not pointing th the right component.
            I get
            “name”: “Unauthorized”,
            “message”: “You are requesting with an invalid credential.”,
            “code”: 0,
            “status”: 401,
            “type”: “yii\\web\\UnauthorizedHttpException”
            here is the behavior
            $behaviors = parent::behaviors();
            $behaviors[‘authenticator’] = [
            ‘class’ => HttpBearerAuth::className(),
            ‘only’ => [‘dashboard’, ‘updateinformation’, ‘logout’],

          2. I tried to move it to my my module init but no luck
            class Module extends \yii\base\Module {
            //public $controllerNamespace = ‘api\modules\v1\controllers’;
            //public $modelClass = ‘api\modules\v1\models\User’;

            public function init(){
            \Yii::$app->set(“user”, [
            ‘class’ => ‘yii\web\User’,
            ‘identityClass’ => ‘api\modules\v1\models\User’,
            ‘enableAutoLogin’ => false,
            ‘enableSession’ => false,
            ‘loginUrl’ => null,
            // more code

          3. I reread all your comments and it seems to me that your applications structure is a lot more different than mine. It would be too difficult to debug it in the comments section. Can you upload your code to GitHub so I could have a look?

  8. Please keep on. :)) Pllleeease! The question seems to be: Should we use angular for everything (validation, rules, and even model (model.js)), and Yii only provides the basic Server needed connection, or should we use Yii and benefit from integrated things?

    1. You probably already know that server side validation isn’t optional whereas client side validation is nice to have, but it isn’t an absolute necessity. If you can afford to have it – great. Client side models – absolutely. In fact it is useful to have as much as possible on the client side without creating vulnerabilities in your application or exposing important domain logic that has to stay secret.

      It considered a good practice to have a server for static content (e.g. html, js, css is served from here: and a separate API server (e.g. In this scenario Yii would live on the API server and its responsibilities would be – handling domain logic and providing an interface to the data. This is also great, because you can use the API server for a native mobile app if you will ever consider making one.
      But there is a problem with this scenario. Yii doesn’t work well with Cross-Origin Resource Sharing in combination with authentication due to a lack of functionality that would handle Preflight Requests before authenticating the user. This problem was described at least 6 months ago HERE, and hasn’t yet been fixed. I described the problem in detail and suggested a potential solution for this problem over a month ago HERE, but it seems to me this is not a priority for the core developers at the moment.

      Unless your application will live on one domain don’t use Yii, to build a REST API with authentication, until this is fixed.

    1. That’s a shame. Sadly I can’t provide free private support. When I encounter a difficult bug and all else fails I use xdebug and PHPStorm to pinpoint the problem. I suggest you try it too. Nothing beats the feeling of solving a difficult problem yourself. Good luck!

  9. its a nice tutorial ,but i am having problem in dashboard action because in my config enable sessionlogin is false .I want to use the oAuth (token based authentication) in my application.

  10. Thanks for great tutorial,it works great for me, but how can i improve it to stay logged-in after closing browser tab? Now, if you close the tab you should enter login\password again.

    1. If you want to add Remember Me functionality you can store the token in a Cookie instead of Session Storage.
      For the sake of simplicity of this tutorial I have sacrificed security.
      If you want your app to be secure it will require more than just saving the token in a Cookie.
      Significant changes will be required for the backend as well.

  11. Hello You forgot writing about uses.

    use Yii;
    use common\models\LoginForm;
    use frontend\models\ContactForm;
    use yii\filters\ContentNegotiator;
    use yii\web\Response;
    use yii\filters\AccessControl;
    use yii\rest\Controller;
    use yii\filters\auth\HttpBearerAuth;

  12. Hi,
    I can set up this application after downloading codes. The problem is after I login by using demo/demo, the page is redirected to dashboard page correctly, and showing access_token. But
    the dashboard and logout links in the top nav bar are not shown.
    It seems the codes:
    $scope.loggedIn = function() {
    return Boolean($window.sessionStorage.access_token);
    didn’t work and didn’t remove the ng-hide from the class attributes of dashboard and logout links.

    Could you please sort out for me.


    1. That is very strange. I’ve found the time and reinstalled it locally to see if it will work.
      Apart from some asset problems, everything was working fine.
      The way Yii does assets is quite unusual.

  13. спасибо вам за урок. У меня вопрос по поводу авторизации. Как сделать чтобы пользователя не выкидывало из сессии спустя какое то время?

    1. I am sorry that in Russian.

      Thank you for the lesson. I have a question about the authorization. How to make that user is not thrown out of the session after a while?

  14. Hey Alexander.
    Nice article.
    Actually, i’m confused about url in angular controller.
    You said in this article that you enable pretty url so in angular controller you access yii url just /api/dashboard or /api/login or /site/captcha.
    I tried to access url in postman chrome extension but i got wrong url to get web service.
    I tried to access localhost/angularjs-yii2-authentication/frontend/web/partial/api/dashboard or localhost/angularjs-yii2-authentication/frontend/site/captcha but the result was 404 not found.
    What is full url from /api/dashboard or /site/captcha?
    Thanks in advance.

  15. Hi in the tutorial you configure the access rules so that only authenticated users are able to enter the dashboard and you use ‘@’ for this purpose.

    My question is: if the Yii api is stateless (RESTful) how this can work since the user session is not mantained after user is logged in, how can we check a user is authenticated? Maybe adding some other specific rules?

  16. Hi Good tutorial, it helped me a LOT! We your users deserve a part 3: ‘RBAC Authorization’ explaining how to implement RBAC authorization like for restful apis.

  17. Thanks for great tutorial,it works great for me, but when we add access token manually in browser session storage the dashboard was accessed without login.

  18. Hi i downaload the code from github and deploye. i m getting blank page when i pass this url “http://localhost:8088/angularjs-yii2-part-2-authentication-master//frontend/web/#/login”

Leave a Reply