Cookie Authentication In A AngularJS WordPress Theme

Hello beautiful readers! Glad to see you again so soon. Today I’d like to share my findings about cookie authentication and our AngularJS WordPress theme. I’ll show you how exactly to get the cookie authentication working with the theme, and we’ll create a simple AngularJS custom directive to display the user name if she/he is logged in.

What I’ll be showing you in this post I actually spent over two months to figure that out (duh!). Eventually to my surprise that cookie authentication is quite simple when we are just building WordPress themes or plugins.

I was blind to the WP API documentation which have stated everything a developer need to know about cookie authentication and WP API, but once I sorted things out, it can’t be even more clear. So I highly recommend anyone who’s interested in this topic, be sure to check the docs out (as much more times as you need)!

If you’re new here, I started this Building Themes With AngularJS and WP REST API series since last November. In last tutorial I talked about how to upgrade the theme to work with WP API v2, you should download the latest project files from the GitHub repo. Now I’m ready if you are, let me start by introducing you how the cookie authentication works in WordPress.

Cookie authentication in WordPress

When we log in to the WordPress dashboard, this action will trigger the wp_signon(), which then uses the wp_authenticate() to check our login information (username and password). If they are validated, the wp_set_auth_cookie() will be invoked that various authentication cookies will be set.

So every time when functions like get_current_user_id() or wp_get_current_user() are used to check if there’s a logged in user visiting a WordPress site, all validation functions hook to determine_current_user filter will be applied, including wp_validate_auth_cookie().

As long as those cookies get validated, the authenticated user will be granted permissions to perform certain actions in WordPress core, themes or plugins. Take WP API for instance, a logged in user can access his own basic account information by visiting the route wp-json/wp/v2/users/me, and get detailed information from route wp-json/wp/v2/users/[HIS-OWN-ID]?context=edit.

There’s a key point I’d like to reminder you again that, this project (the AngularJS theme we’re building) is still a standard WordPress theme. That said when a logged in user makes requests with WP API in our theme, he can pass permission checks automatically for routes like wp-json/wp/v2/users/me.

But things won’t stop here because internet is a dangerous place, bad guys out there are trying to fool us with phishing links, so they can gain access to our WordPress site and make everything a mess.

Phishing links are a typical method that hackers use to perform malicious exploits called CSRF (Cross Site Request Forgery). Luckily WordPress has developed a standard technique to prevent users from getting exploited by CSRF, which is called nonces (numbers used once).

Nonces in WordPress

Though the name may imply, nonces actually can be used more than once in their lifetime. Long story short, nonces are used to validate if a request is intentionally made by the current logged in user. They are generated based on those authentication cookies and a time-dependent variable, that makes them dynamic and can’t be forged by a 3rd party website.

You must be familiar with URLs from your WordPress dashboard look like:

http://URL/wp-admin/post.php?post=POST-ID&action=trash&_wpnonce=NUMBERS-LETTERS

This is very crucial that without the nonce, any hacker can send you an email with the delete link (and a kitty photo to trick you to click). You might just delete your own post without notice.

Now we know nonces are very important, but how can we implement this technique in our AngularJS theme?

Nonces in our AngularJS theme

According to WP API documentation on cookie authentication (emphasis added):

For developers making manual Ajax requests, the nonce will need to be passed with each request. The API uses nonces with the action set to wp_rest. These can then be passed to the API via the _wp_rest_nonce data parameter (either POST data or in the query for GET requests), or via the X-WP-Nonce header.

Let’s take a closer look at the exactly function rest_cookie_check_error() from WP API v2 beta-4:

This function is beautifully coded and very self explanatory. From line 32 to 37, WP API checks for two values – $_REQUEST['_wp_rest_nonce'] and $_SERVER['HTTP_X_WP_NONCE'], if they don’t exist, WP API will set the current user to 0 and return true (means no authentication error but it’s an unauthenticated request). Otherwise, the nonce will get verified and return an WP_Error object.

So apparently now we have two approaches to pass the nonce:

  1. Passing it as another url parameter so the routes would be like: wp-json/wp/v2/posts/ID?context=edit&_wp_rest_nonce=NONCE.
  2. Sending the nonce (named X-WP-Nonce) in the request headers with each API call.

The second approach sounds a bit more challenging, right? I encourage you to take this approach with me that you’ll learn an in-depth technique in AngularJS about passing values with each request. In our case, that’ll be the nonce.

I hope you are ready with the theme files from GitHub, just open your favorite text editor and let’s start with functions.php.

Creating a custom directive to display the current user name

Step1: Adding the nonce to our theme

Just like how we passed the _partials directory to our JavaScript, we do that again by adding a new property to myLocalized object. We name it nonce (you can change the name) and use wp_create_nonce() to create the nonce with the action set to wp_rest (you definitely can’t change that).

Step 2: New currentUser object and getCurrentUser function in WPService factory

In our WPService factory, we add a new property currentUser to WPService object and set it to an empty object. We also create a new function getCurrentUser that sends a request to wp-json/wp/v2/users/me to get basic account information from WP API. If the request is successful, we’ll then assign the response to the currentUser object.

Step 3: Sending the nonce in the request headers

To set the request headers in AngularJS, we’ll do it with the $httpProvider.interceptors. The AngularJS documentation introduces interceptors as below (emphasis added):

For purposes of global error handling, authentication, or any kind of synchronous or asynchronous pre-processing of request or postprocessing of responses, it is desirable to be able to intercept requests before they are handed to the server and responses before they are handed over to the application code that initiated these requests.

The interceptors are service factories that are registered with the $httpProvider by adding them to the $httpProvider.interceptors array. The factory is called and injected with dependencies (if specified) and returns the interceptor.

Now let’s add the interceptor service factory to the configuration block in our app. The following gist shows that we set the X-WP-Nonce header to the nonce we’ve created whenever an API request is made (at line 12):

Step 4: Creating a custom directive to say hello to a logged in user

Creating a custom directive in AngularJS is very easy, you can go back to check out my previous tutorial about it again. The gist below shows that:

  • We create a custom directive to “say hello”.
  • We can use the directive with syntax like “<say-hello>” or “<div say-hello>“.
  • We need to create a new template file say-hello.html in our _partials directory.
  • In this custom directive, we’ll call WPService.getCurrentUser() function to update the WPService.currentUser object.

In the template file say-hello.html, we test if data.currentUser.id is true, if so, we display our hello message.

And lastly, we just add the custom directive <say-hello></say-hello> to the beginning of main.html, or any place we’d like to show the hello message. Boom! We’re done!

Cookie authentication in a AngularJS WordPress theme

Cookie authentication, nonces and AngularJS $httpProvider.interceptors

Although we were going to talk about cookie authentication in this tutorial, we actually spent more time on figuring out how to work with WordPress nonces in our AngularJS scripts. You should get the idea that getting a WordPress theme authenticated with WP API is really simple and don’t really need to worry anything about the cookies as a matter of fact.

You can also take this tutorial as a warm-up if you’d like to build a standalone AngularJS app with WP API in the future. For example, you might be interested in trying some third party authentication methods like JWT Auth for your standalone app, in that case you’ll need to use $httpProvider.interceptors to pass cookies and Bearer Token.

As always, you can download the final project files from the GitHub repository. I hope you enjoy the whole process as I do, or at least learn some new tricks to solve your own issues. Just email (yoren [at] 1fix.io), tweet me (@1fixdotio) or leave a comment below if you get any question regarding my tutorials.

See you soon in my next post!

13 responses

  1. Javier Torron Avatar
    Javier Torron

    Hi,

    Since the first time I get to your blog I am fighting against AngularJS and WordPress. I really love your idea, REALLY! Thanks a lot for your work and your posts.

    Now I have a problem and I am going mad. Begging for your help desperately. Thats the case:
    I am developing an assistant to create ads in a wordpress page, handled by AngularJS. In backend I created a new menu element whcich loads the Angular script and begins the assistant. In the third step of that I need to call wp.media to select an image from wordpress media library. So, I put a textbox with ng-model to a scope variable and open the wp.media window. All works so fine, the window opens, the image is selected and everything. But my textbox is empty, so the ngModel variable still empty. If I click the open button again then the textbox have an URL (even if I close the window without selecting any image).

    That means that the scope variable is one step behind you, first time is empty, second time you open the window the variable is filled with your first choice, and again and again.

    So, can you do one of your incredible tutorials to see how it can be done? Or, if you prefer, can you help me with my code?

    1. Yoren Chang Avatar
      Yoren Chang

      Hey Javier, really appreciate you like my work. I’d love to assist with you so just send your code to yoren [at] 1fix.io. I’ll see what I can do and reply to you as soon as I can. Talk soon!

  2. bobsilon Avatar

    Hi Yoren,
    Amazing Article, very very helpful. This works fine when user logged in with WordPress login.php page. But what about the state when we want user, login through our app?

    I think we should create new route which receives the credentials like username and password, then using the wp_signon() we log in that user. But the problem is how to transfer the generated cookie to the app? Is there any way that we can create cookies, based on given “username” and “password” directly in our app?

    1. Yoren Chang Avatar
      Yoren Chang

      Hey Bobsilon, as the last few paragraphs in my post said, if you’re building an login form in your AngularJS theme, then you don’t have to worry about the cookies. But if it’s a standalone AngularJS app, you’ll need third party plugins like JWT Auth or OAuth to get your app authenticated with WP API.

      I plan to write a post about building a login form directive in our AngularJS theme. But in fact, a PHP/AJAX login form is good enough and can definitely work with our theme.

      1. bobsilon Avatar

        wow, Thanks for fast answering 🙂
        Great, I intensively waiting for next article of this amazing series.

  3. Hi again Yoren,
    I’ve done with JWT, but needs to .htaccess and config files modifications, which we don’t want final users do something more than just installing the theme.

    So I went to the OAuth but I still have not finished it. It’s a little bit harder than JWT to implement.
    what about social login like google, is it possible via OAuth plugin?

    1. Yoren Chang Avatar
      Yoren Chang

      Hey Bobsilon,

      So are you trying to build a AngularJS theme (like what I did) or a standalone AngularJS app?

      According to the official document http://v2.wp-api.org/guide/authentication/, we should stick to cookie authentication if we are building a theme.

      It looks like you just need a front end login form, is that correct? If so, I believe any social login can definitely work well with your theme – just it may need a page reload to update the page elements to display WP admin toolbar etc.

      If you are building an AngularJS standalone application, that’s beyond my knowledge about AngularJS. I’d love to hear from you sharing your experience, but very little I can help.

  4. Thanks,

    Yes I want to build an AngularJS theme, but don’t want the page’s reload. It’s better user can login without any page reload, this provide better user experience. Isn’t it? Something like login using a dialog box.
    So the cookie authentication is inefficient, because user should go to the login page, authenticate and then redirect to our angular route.

    Furthermore, I planning for android app for future so As you said it’s better to using OAtuh.

    Best Regards.

    1. Yoren Chang Avatar
      Yoren Chang

      I hope I didn’t mislead you that the cookie authentication can only work with the wp-login.php login page. It can definitely work with any method that generating the right WP cookies.

      As to the page reloads, I just described the common front end login forms often do that – but that definitely not a must!

      Sorry I didn’t make it much clear at first. Feel free to contact me if you’re still interested in this topic.

  5. 你好,我採用wp-oauth。我已經獲取了access_token,但是我訪問/wp-json/wp/v2/users/me?access_token=ah7w9yegbgd7thm5ltror1qpeldmmngfvpcn63pq的時候會自動跳轉到wp-json/wp/v2/user/3的頁面。我想我登入後全部的$http,$resource都能用到這個token,所以我想重寫這個me方法,讓它不跳轉到/user/id,而是直接獲取登入者的資訊

    1. Yoren Chang Avatar
      Yoren Chang

      Hello, 我還沒試過 oauth 方式,不過如果你想要使用同一個 token,應該不是用 URL 方式傳遞才對?如果要重寫 me route 的話,你可以直接創建自己的 custom route 或 endpoint(我查過原始碼,users/me 的 callback function 裡面沒有可用的 hooks)。

  6. Any advice on how to handle a 403 forbidden error? It gives the additional message “Cookie nonce is invalid”. I’m fairly sure I’m sending the token correctly and have the correct cookies in my cookie jar.

    I’m actually working in React, not Angular, but came upon this related tutorial

    1. Yoren Chang Avatar
      Yoren Chang

      Hey Peter,

      For all authentication methods involved with WP API, I would recommend going with JWT https://tw.wordpress.org/plugins/jwt-authentication-for-wp-rest-api/. It’s the only way I found pretty easy to adapt and rarely bump into issues.

Leave a Reply

Your email address will not be published. Required fields are marked *