Using oAuth in WordPress Plugins, Part 2: Persistence!

September 3, 2015

In my previous post we setup a basic oAuth connection between our plugin and Google. Within a post, you could log into Google and get info about a YouTube video to import into your post.

Unfortunately, this means that every time you want to do anything with YouTube you have to log in again. What if you want to schedule some task with YouTube? If you aren’t in the office, how do you log in? In the use case I described in the previous post, the PBS NewsHour staff needed to be able to schedule an automated nightly change to ‘private’ status for certain YouTube videos, so they had to have a means for persistent login.

oAuth has exactly the mechanism we want to make this work – the ‘refresh token‘. Last post we were using the ‘implicit grant flow‘ model for making oAuth work. That doesn’t require any server-side storage mechanism, or really any server logic at all, but for serious business we want to use the ‘authorization code flow‘, which is more complex but allows us to stay logged in FOREVER once the user has given his initial consent.

The ‘implicit grant flow’ always requires the user to login directly with Google (or whatever oAuth provider they’re using), and only keeps them authenticated for the lifetime of the access token – typically 1 hour – after which the user has to open up a browser window and login again. This is a security tradeoff because the ‘client id’ for the app the user is logging into is always visible (it’s part of the URL for the login window). A malicious person could possibly display a login to the app on some different web page with some DNS trickery and get that access token for their own uses.

The oAuth authorization code flow is significantly more secure because it makes the authentication process go through an additional server-to-server request that will include both the ‘client id’ and the ‘client secret’ values that Google gave us in the ‘app’ setup. The client id and client secret act as a username/password combination – that secret code should never be exposed to the general public. In the authorization code flow, the user goes to the oAuth endpoint as before to log in, but this time we get a ‘grant code’ back instead of an access token. We then pass that to our server, and our server will send the grant code to Google along with the ‘client id’ and the ‘client secret’, and Google can be much more certain that the request is being made from a server configured by the app owner.

When Google sees that the client id, client secret, and grant code are all valid, Google will then send back an access token and a refresh token. The implicit grant flow also gave us an access token, and this access token is exactly the same – is is specific to the user who authenticated, can be used to make requests on behalf of that user, and expires after some set time (usually 1 hour).

All that is new is the refresh token.

The Refresh Token is MAGIC

The refresh token has only one function: to be sent (along with the client id and client secret) to Google to request a new access token without any user interaction or expiration. Refresh tokens don’t typically expire, though some oAuth providers invalidate them once they’re used – in that case a new refresh token will be issued at the same time. Google does NOT invalidate the refresh tokens upon use, nor do they expire with time.

That brings us to an important security note: in most oAuth implementations, if I request a refresh token and save it, then a couple months later I change my password, then a couple months after that I try and use my refresh token, I will get a valid access token with all the rights I originally granted, even though that refresh token was generated when I had a different password MONTHS AGO.

That is why it is important to limit the scope of our oAuth grant to the minimum required – if you include in your scope the ability to change your password or other core user info, you’ve opened a huge hole into your account.

Switching to the Authorization Code Flow

In our ‘implicit flow’ JavaScript in the last post, we requested a token directly. We only need to change the JavaScript a little to request a grant code instead. Replace ‘response_type=token’ with ‘response_type=code&access_type=offline’. The ‘code’ response type tells Google that we’re using the authorization code flow, and the ‘access_type=offline’ tells Google that we will eventually want them to give us a refresh token.

Then, instead of directly parsing what we get back in the JavaScript for an access_token value etc (because they won’t be there), look for a ‘code’ value, and use AJAX to pass the ‘code’ to WordPress to some PHP.

Here’s our updated requestGoogleoAuthCode function:

  function requestGoogleoAuthCode() {
    var OAUTHURL = '';
    var SCOPE = 'profile email openid';
    var popupurl = OAUTHURL + '?scope=' + SCOPE + '&client_id=' + GOOGLECLIENTID + '&redirect_uri=' + GOOGLECLIENTREDIRECT + '&response_type=code&access_type=offline&prompt=select_account consent';
    var win =, "googleauthwindow", 'width=800, height=600'); 
    var pollTimer = window.setInterval(function() { 
      try {
        if (win.document.URL.indexOf(GOOGLECLIENTREDIRECT) != -1) {
          var response_url = win.document.URL;
          var auth_code = gup(response_url, 'code');
          // We don't have an access token yet, have to go to the server for it
          var data = {
            action: 'foo_finish_code_exchange',
            auth_code: auth_code
          $.post(ajaxurl, data, function(response) {
            google_access_token = response;
      } catch(e) {}    
    }, 500);

Using WordPress PHP to close the loop with Google

Now that we’ve gotten an authorization code from Google, we want to trade it in for an access token and a refresh token, and store the access token’s expiration date. After we’ve done the initial authorization code exchange, anytime we need to get access, we’ll check the expiration date to see if the access token is still valid, and if it needs renewal use the refresh token, client id, and client secret to get a new access token and get the new expiration date.

I’ve got a function we’ll call in either case called ‘set_google_oauth2_token’ that we’ll call in either case and that I’ll list later.

When our initial JavaScript login and authorization request happens, it sent that ‘auth_code’ to our admin_ajax function.

This PHP handles the admin_ajax end. My plugin keeps everything in a class file, hence the ‘$this->’.

// setup the wp ajax action 
add_action( 'wp_ajax_foo_finish_code_exchange', array($this, 'finish_code_exchange') );

// send the action to our reusable function
public function finish_code_exchange($auth_code) {
  echo $this->set_google_oauth2_token($auth_code, 'auth_code');

Here’s the function we’ll use both for code exchange and later for refreshing access tokens:

  private function set_google_oauth2_token($grantCode, $grantType) {
   /* based on code written by Jennifer L Kang that I found here
   * and modified to integrate with WordPress and to calculate and store the expiration date.
    $settings = get_option('foo_oauth_demo_settings', true);
    $success = true;	
    $oauth2token_url = "";
    $clienttoken_post = array(
      "client_id" => $settings['google_app_client_id'],
      "client_secret" => $settings['google_app_client_secret']

    if ($grantType === "auth_code"){
      // The "auth_code" grant type is to do the initial code exchange
      $clienttoken_post["code"] = $grantCode;	
      $clienttoken_post["redirect_uri"] = $settings['google_app_redirect_uri'];
      $clienttoken_post["grant_type"] = "authorization_code";
    if ($grantType === "refresh_token"){
      // The "refresh token" grant type is to use a refresh token to get a new access token
      $clienttoken_post["refresh_token"] = get_option('foo_google_refresh_token', true);
      $clienttoken_post["grant_type"] = "refresh_token";
    $postargs = array(
      'body' => $clienttoken_post

    $response = wp_remote_post($oauth2token_url, $postargs );
    $authObj = json_decode(wp_remote_retrieve_body( $response ), true);
    if (isset($authObj[refresh_token])){
      $refreshToken = $authObj[refresh_token];
      $success = update_option('foo_google_refresh_token', $refreshToken, false); 
      // the final 'false' is so we don't autoload this value into memory on every page load
    if ($success) {
      $success = update_option('foo_google_access_token_expires',  strtotime("+" . $authObj[expires_in] . " seconds"));
    if ($success) {
      $success = update_option('foo_google_access_token', $authObj[access_token], false);
      if ($success) {
        $success = $authObj[access_token];
    // if there were any errors $success will be false, otherwise it'll be the access token
    if (!$success) { $success='it failed'; }
    return $success;

At the end of that code exchange, we have WordPress options set for ‘foo_google_access_token’, ‘foo_google_access_token_expires’, and ‘foo_google_refresh_token’. The next time we want to use an access token:

  public function get_google_access_token() {
    $expiration_time = get_option('foo_google_access_token_expires', true);
    if (! $expiration_time) {
      return false;
    // Give the access token a 5 minute buffer (300 seconds)
    $expiration_time = $expiration_time - 300;
    if (time() < $expiration_time) {
      return get_option('foo_google_access_token', true);
    // at this point we have an expiration time but it is in the past or will be very soon
    return $this->set_google_oauth2_token(null, 'refresh_token');

Now in my plugin code, wherever I want to get an access token that’ll be valid for at least the next 5 minutes, I just do

  echo $this->get_google_access_token();

Revoking the refresh token

Remember that the refresh token is a very powerful thing – once created, you can always get a new access token, even if the account password has changed. What we want to do is make a PHP function that revokes the tokens and deletes the options from the database.

  public function revoke_google_tokens() {
    /* This function finds either the access token or refresh token
     * revokes them with google (revoking the access token does the refresh too)
     * then deletes the data from the options table
    $return = '';
    $token = get_option('foo_google_access_token', true);
    $expiration_time = get_option('foo_google_access_token_expires', true);
    if (!$token || (time() > $expiration_time)){
      $token = get_option('foo_google_refresh_token', true);
    if ($token) {
      $return = wp_remote_retrieve_response_code(wp_remote_get("" . $token));
    } else {
      $return = "no tokens found";
    if ($return == 200) {
      return true;
    } else {
      return $return; 

This changes how we ‘logout’ somewhat, also. Incidentally, this is also how we would deal with a refresh token that stopped working – the most likely place being the Google Account Settings page.

Now, our JavaScript will call an admin-ajax function instead of directly hitting Google.

  function logoutFromGoogle() {
      url: ajaxurl,
      data: {
        action: 'foo_logout_from_google'
      success: function(resp) {
        google_access_token = '';

And here’s the admin-ajax function to call the revoke script:

 add_action( 'wp_ajax_foo_logout_from_google', array($this, 'logout_from_google') );

 // wrapper for wp_ajax to point to reusable function
  public function logout_from_google() {
    $response = $this->revoke_google_tokens();
    if ($response === true) {
      $response = "success";
    echo $response;

Using oAuth to do something useful with YouTube

In the previous post, we were including our login/logout JavaScript on every post editing page. Any time you reloaded the page, or visited a different page, you’d have to log in again. Now we’ve got persistent values for being logged in or out, so you should move the login/logout links and JavaScript to your settings page, where you can set it and forget it.

But I still want to be able to pull in YouTube information onto an individual post page. As it happens, the JavaScript function we built in the previous post, getYouTubeVidInfo(), doesn’t have to change at all; you’d still include it on the post page. But now, you’ll set that ‘google_access_token’ JavaScript variable by making the get_google_access_token() call, which will always return a valid, up-to-date access token even if you haven’t personally logged in to Google for months.

<script language=javascriptt>
  var google_access_token = "<?php echo $this->get_google_access_token(); ?>"; 

JavaScript functions to display something are nice, but the real power that we’ve been building up to in this series has been the ability to interact with our YouTube channel when we AREN’T around to log in.

Here’s a function that updates the status of a YouTube video – my original use case for the PBS NewsHour.

  public function submit_youtube_expire_request($videoid, $status){
    $access_token = $this->get_google_access_token();
    if (! $access_token) {
      return false;
    $bodyargs = array(
        "id" => $videoid,
        "kind" => "youtube#video",
        "status" => array(
          "privacyStatus" => $status
    $body = json_encode($bodyargs);
    $url = "";
    $args = array(
      "method" => "PUT",
      "headers" => array(
        "Authorization" => "Bearer " . $access_token,
        "Content-Type" => "application/json"
      "body" => $body
    $request = wp_remote_request($url, $args);
    if (wp_remote_retrieve_response_code($request) != 200){
      error_log("privacy change failed : " . wp_remote_retrieve_body($request));
      return false;
    return json_decode(wp_remote_retrieve_body($request));

The YouTube Data API documentation is excellent if you’re interested in more complex tricks.

Now that we can do things without directly logging into Google, there are all sorts of possibilities. We could…

  • Setup a wp_schedule_event to switch a video from ‘private’ to ‘public’
  • Retrieve the latest comments on a video
  • Update the description of the video
  • etc, etc….

More possibilities

Beyond YouTube, oAuth can let you automate your interactions with all sorts of remote services. Services like Facebook, Twitter, and StackExchange all use oAuth; while the implementation details of setting up the ‘apps,’ the endpoint URIs, data APIs, and other details will all be a little different, the core structure and concepts are consistent.

Integrating those interactions with your WordPress site will make life for your users much simpler than having to go multiple places to manage their workflows. I hope you’ll let us know how you’ve made the most of oAuth.

Below is an updated version of our functioning sample plugin that implements all of the code in this post.

Digital at The WNET Group is not responsible for your or any third party’s use of code from this tutorial. All the information on this website is published in good faith “as is” and for general information purposes only; WNET and IEG make no representation or warranty regarding reliability, accuracy, or completeness of the content on this website, and any use you make of this code is strictly at your own risk.