Blog

Inside the Minds of the Machine

APIs, WordPress Tips and Tricks

Using oAuth in WordPress Plugins, Part 1: The Basics

[UPDATE: — part 2 of this post is here.]

WordPress is pretty good at making it easy to talk to outside web services. For instance, oEmbed lets you drop a link to a YouTube video onto your post and viola! there’s your video player dynamically rendered. But what if I wanted to manage my YouTube videos from within my WordPress site, how would I do that? Or automatically push all of my new WordPress posts to my Facebook profile or Twitter page, or display Google Analytics data in a dashboard in WordPress?

The answer is oAuth. oAuth – to be specific, we’ll talk about oAuth 2.0 – is a standard protocol that lets you log into an external, oAuth-enabled service provider like Facebook, Google, Twitter (fun fact: it was originally known as ‘Twitter oAuth’ because Twitter pioneered it) or many others from within your site and do things as if you were on that site. There are lots of WordPress plugins that use oAuth to let your users log into your website using their social media accounts, which is nice because it means the user has one less login to remember and manage. But if you want to allow your users to actually interact and manage the data in those social media accounts you need more than those plugins provide, and there doesn’t seem to be is a straightforward explanation of how you’d get oAuth working within your WordPress admin. Lets try and correct that!

The use case I mentioned at the start of this was managing my YouTube videos from within my WordPress site. This begs the question, “Why wouldn’t I just go to YouTube.com to manage my YouTube videos?”

Our client, the PBS NewsHour, posts 2-10 videos to YouTube every day as part of a process that includes responding to breaking news, writing and editing transcripts and blog posts, managing discussions and social media, and also posting those same videos to PBS’s proprietary COVE video hosting system. We built into their WordPress admin tools to upload and manage those YouTube videos from within the post editor, so the video management is integrated into each post. This greatly simplified their workflow, and lets them concentrate on reporting the news.

We’ll start with just making the connection to YouTube work.

  • oAuth depends on a redirect URI or callback. This can and should be a blank page on your website.
  • YouTube is part of the Google ecosphere, so I created a Google ‘app’ to define and manage the oAuth connection.
  • The user will need to interactively login and approve access with Google, so I created a JavaScript file to manage a popup window and pass some data back to the page.

The oAuth callback

The oAuth flow works like so: put a link on your website to Google’s oAuth endpoint with arguments for ‘scope’ and ‘redirect’ (and some other things). This endpoint shows the user a Google login screen, and once the user has logged in they are asked to give permission for the ‘scope’ (such as ‘manage YouTube account’), and when they do the window redirects to the address you gave as the ‘redirect’ argument, though Google will add some query string arguments to it. You need to create on your website a page that lives at the URL you put in the redirect argument. For our purposes, this can be a completely blank page – no header, footer, 0 bytes, doesn’t need a .php extension, it just needs to be a URL that you can put in a browser window and not get an error message. Because we want it to load fast, a blank file called ‘oauth2callback’ within the folder you’re building your plugin will serve nicely.

For example, if you are developing your plugin named ‘foo’ on your website ‘example.com’ and you put your blank oAuth callback file named ‘oauth2callback’ in that plugin folder, the URL you should be able to load into your browser would be ‘http://www.example.com/wp-content/plugins/foo/oauth2callback’

While you’re in your plugin, you should also create the following settings to store some data from the next step – they’re all text fields.

  • Google App Redirect URI – ‘foo_google_app_redirect_uri’: your oAuth callback URL.
  • Google App Client ID – ‘foo_google_app_client_id’
  • Google App Client Secret – ‘foo_google_app_client_secret’

Note: I’m not going to explain how to create a basic WordPress plugin or create a plugin settings page, but I’ll include a complete working plugin that includes the settings page.

Also, I’ve prefaced these settings with ‘foo_’ because I’m pretending our plugin is named ‘foo’, so change the prefix to something that matches your plugin. You can of course name these differently, store them in a single setting as an array, etc.

The Google ‘app’

I keep putting quotes around ‘app’, and I’m going to continue to do so because this Google ‘app’ is nothing but a place in the Google ecosphere where you manage the permissions of the oAuth connection between your website and Google. These ‘apps’ are FREE (within certain quota thresholds), don’t require coding, and users will never see it. Google provides a comprehensive guides to making and managing them at https://developers.google.com/console/help/. For our project we will

  1. Login to https://console.developers.google.com with your Google account. This login is just for the ‘app’ creation and management – the people using your ‘app’ login using their own accounts.
  2. Create a project. On the ‘Getting started’ page, this is called an ’empty project’. Google has lots of wizards in this console for selling you on Cloud Storage, BigQuery, etc, you don’t need any of that for our uses.
  3. In the left column see ‘APIs & auth’, and pick the APIs for Google services you want to use. To get basic user info such as ‘name’, you’ll need the Google+ API. I also needed YouTube’s Data APIs. Find each API you want to use in the list, click on the link, ‘enable’ the API, and if you have time click the ‘Explore this API’ link to see all the fun coding options. If you click on ‘Quotas’ for the API, you’ll see that for the YouTube Data API the base free quota is 50 MILLION ‘units’ (requests) per day, so if you are busting that quota that’s a problem you want to have!
  4. Now click on ‘Consent Screen’ and enter something into ‘Product Name’. We can come back to this later, but we have to do this before the next step.
  5. Next, click on ‘Credentials’ to see a section about OAuth. Create a new Client ID for a ‘Web application’. You’ll want to enter your website URL in the ‘Authorized JavaScript origins’, and the path to your oAuth callback on your website in the ‘Authorized redirect URIs’. Pro tip: if you’re developing on your desktop, ‘http://localhost/’ is a valid origin, and you can always remove it from the list later. Once you’ve created the Client ID, save the values for ‘Client ID’ and ‘Client Secret’ in those WordPress settings you created earlier.

Using JavaScript to make the login popup and get the access token

We’re going to open a popup window to Google so the user can login and authorize your ‘app’. (Getting tired of those quotes yet?) Popup windows are ugly and inelegant, but they are a necessity here because the oAuth security model requires that any window where you ask a person to give their login credentials show the URL. Otherwise, some bad actor put up a form and ask you to put in your Google username and password and hijack those values. So no pretty lightbox approach for us.

We’ll be using JavaScript to construct a URL from Google’s oAuth endpoint URL and some query string arguments:

  • scope – in this case, ‘profile email openid https://www.googleapis.com/auth/youtube’ (URLencode that). ‘profile email openid’ is the scope for who you are, and the googleapis.com URL is for YouTube’s API.
  • client_id – the Client ID from your Google ‘app’.
  • redirect_uri – the oAuth callback URI. It needs to be on the same server as this JavaScript file, because of the ‘same origin policy’.
  • response_type – we’re requesting an access token, so the value is ‘token’. This is only possible in the ‘implicit flow’
  • Optional: login_hint – if there’s a specific user account you want to require the user to login as, pass this argument in. You can alternately use ‘prompt=select_account’ if you want to give the user the option to pick what Google account they have access to to use.

We’ll then use JavaScript to open a new popup window to that URL. The user logs in and authorizes our app, and then Google redirects the browser in that popup window to the redirect_uri you specified with a query string with access_token and expires_in variables in it – something like:

http://www.example.com/wp-content/plugins/foo/oauth2callback?access_token=adsfasfsaf123131231&expires_in=3600

We’ll sit and check every 500 milliseconds to see what the URL of that popup window is. Once the window’s URL begins with the same value as we specified for the redirect_uri, we’ll take whatever the query string was, parse out the access_token and expires_in values, and close the popup.

First, however, lets use a little PHP on the page we’re calling this from to retrieve and set the Google Client ID and Google Redirect URI settings and make them JavaScript variables. The rest of the code you see should appear in a ‘post’ screen as part of a metabox that your plugin generates.

<script language="javascript">
// We'll declare the google_access_token variable outside of our jQuery to make it easier to pass the value around.
var google_access_token = '';
jQuery(document).ready(function($) {
  var GOOGLECLIENTID = "<?php echo get_option('foo_google_app_client_id', true); ?>";
  var GOOGLECLIENTREDIRECT = "<?php echo get_option('foo_google_app_redirect_uri', true); ?>";
  // we don't need the client secret for this, and should not expose it to the web.

Next, we’ll setup the JavaScript function that makes the request.

  function requestGoogleoAuthCode() {
   var OAUTHURL = 'https://accounts.google.com/o/oauth2/auth';
   var SCOPE = 'profile email openid https://www.googleapis.com/auth/youtube';
   var popupurl = OAUTHURL + '?scope=' + SCOPE + '&client_id=' + GOOGLECLIENTID + '&redirect_uri=' + GOOGLECLIENTREDIRECT + '&response_type=token&prompt=select_account';
   var win =   window.open(popupurl, "googleauthwindow", 'width=800, height=600'); 
    var pollTimer   =   window.setInterval(function() { 
      try {
        if (win.document.URL.indexOf(GOOGLECLIENTREDIRECT) != -1) {
          window.clearInterval(pollTimer);
          var response_url =   win.document.URL;
          google_access_token =   gup(response_url, 'access_token');
          win.close();
          getGoogleUserInfo(google_access_token);
        }
      } catch(e) {
      }
    }, 500);
  }

  // helper function to parse out the query string params
  function gup(url, name) {
    name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
    var regexS = "[\\?#&]"+name+"=([^&#]*)";
    var regex = new RegExp( regexS );
    var results = regex.exec( url );
    if( results == null )
      return "";
    else
      return results[1];
  }

Once we’ve got the access token, just as a proof of concept, lets get some basic user info about the person who has logged in, and display it on the page.

  function getGoogleUserInfo(google_access_token) {
    $.ajax({
      url: 'https://www.googleapis.com/plus/v1/people/me/openIdConnect',
      data: {
        access_token: google_access_token
      },
      success: function(resp) {
        var user = resp;
        console.log(user);
        $('#googleUserName').text('You are logged in as ' + user.name);
        loggedInToGoogle = true;
        $('#google-login-block').hide();
        // let's show a logout link
        $('#google-logout-block').show();
      },
      dataType: "jsonp"
    });
  }

At the end of that function we made a logout link appear. We do this by loading up Google’s logout endpoint in a hidden iframe.

  function logoutFromGoogle() {
    $('#googleAuthIFrame').attr("src", "https://www.google.com/accounts/Logout");
    $('#google-login-block').show();
    $('#google-logout-block').hide();
    $('#googleUserName').text('You are not logged in');
    // null out the access token since we've logged out
    google_access_token = '';
  }

Finally, we need to set up our click events.

   $(function() {
    $('#google-login-block').click(requestGoogleoAuthCode);
    $('#google-logout-block').hide();
    $('#google-logout-block').click(logoutFromGoogle);
   });
});
</script>

That’s all the JavaScript, but we need to put some HTML up to interact with it.

<a id="google-login-block">Login to Google </a>
<span id="googleUserName">You are not logged in </span>
<span id="google-logout-block"><a>Logout from Google</a></span>
<iframe id="googleAuthIFrame" style="visibility:hidden;" width=1 height=1></iframe>

Hooray! Now I can log into Google and show my username!

But… didn’t I say something about managing my YouTube videos?

As proof of concept, let’s just get the available data about a video. We’ll take our access_token and use it to get data about a video, then we’ll just dump it all onto the page.

<script language="javascript">
  function getYouTubeVidInfo() {
    var video_id = $('#youtubevidid').val();
    $.ajax({ 
      url: 'https://www.googleapis.com/youtube/v3/videos',
      method: 'GET',
      headers: {
        Authorization: 'Bearer ' + google_access_token
      },
      data: {
        part: 'status,snippet',
        id: video_id
      }
    }).done(function(response) { 
      if (response.items[0].snippet){
        var thisdata = response.items[0].snippet;
        $('#youtubevideodata').html('' + thisdata.title + '
' + thisdata.description); } }); } // Setup a click event for this function $(function() { $('#youtube-get-vidinfo').click(getYouTubeVidInfo); }); </script>

Add some HTML to show an text input for the video id and link to trigger the function.

<input id="youtubevidid" type=text value="5ywjpbThDpE" /><a id="youtube-get-vidinfo">Get Video Info</a>
<div id="youtubevideodata"></div>

Now, when you enter a YouTube video id and click the link, you’ll see the title and description of the video. I’ve defaulted the video id to my favorite video, BaneCat.

But we’re just getting started, really. In part two, we’ll build on what we did here with the more powerful ‘code exchange flow’ that will let us do persistent logins – so our server can do things with oAuth even when the user isn’t present to log in.

Here’s a gist of a complete working ‘foo-v1’ plugin that implements all this code to put a metabox on posts and setup basic settings fields.