This is article 6 of the YouTube API With PHP series.
CODING PATTERN
There are two kinds of coding patterns when working with the YouTube Data API. The first is when working with public data which does not require any user authentication. This only requires the YouTube API key.The second is when working with data private to a channel or a YouTube user. This requires an authenticated token generated by Google after a successful OAuth session.
The first pattern is simple and flows in a linear fashion, the second is a little more roundabout as you will see below. It is important to get familiar with the code flow before we explore the API entities.
You will need the following values from your Google API Console to use in your code. Refer to the Google API Console section to know where to get them from.
- YouTube API key
- Client Id
- Client Secret
- Authorized Redirect URL
But even before working with the API , we have to deal with one more thing. The URLs used with the YouTube API are all https based i.e they use SSL. We will be using PHP curl for all data posting and retrieval.
1.SSL Certificate for CURL
Curl requires special flags to be set for working with SSL urls. By default , any SSL url will require verification when some data is requested from it. So the default usage of curl will fail to get any data as the SSL site will reject it. There is a way around it by sending header data to the SSL site to ignore any verification. This is done by the following options:
CURLOPT_SSL_VERIFYPEER => 1 CURLOPT_SSL_VERIFYHOST=> 0
But this approach does not work with the YouTube API URLs as they require an authentication certificate to be present in the request that is sent. If you are running code from a hosted server or website in an ISP , the chances are , there will already be a SSL certificate installed and you won’t have to worry about it. But in case you are running code from a localhost then you will need a certificate file .
You will find a file called cacert.pem in the data subfolder in the sample code zip, which can be used for doing SSL requests. So the curl options which are needed to work with the YouTube API are
CURLOPT_SSL_VERIFYPEER => 1 CURLOPT_SSL_VERIFYHOST=> 0 CURLOPT_CAINFO => "../../cert/cacert.pem" CURLOPT_CAPATH => "../../cert/cacert.pem"
2.Using Only API Key
Below is a sample code which accesses the details of a Channel. It only needs the API key to do it. Do not worry about what exactly the code is doing. The important thing is to understand the code flow.
<?php error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING ^ E_DEPRECATED); set_time_limit(60 * 3); $g_YouTubeDataAPIKey = "xxxxxx"; $channelId = "UCddiUEpeqJcYeBxX1IVBKvQ"; // make api request $url = "https://www.googleapis.com/YouTube/v3/activities?part=snippet,contentDetails,id&channelId=" . $channelId. "&maxResults=50&key=" . $g_YouTubeDataAPIKey; $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $url, CURLOPT_USERAGENT => 'YouTube API Tester', CURLOPT_SSL_VERIFYPEER => 1, CURLOPT_SSL_VERIFYHOST=> 0, CURLOPT_CAINFO => "../../cert/cacert.pem", CURLOPT_CAPATH => "../../cert/cacert.pem", CURLOPT_FOLLOWLOCATION => TRUE )); $resp = curl_exec($curl); curl_close($curl); var_dump($resp); ?>
The flow is simple here:
3.Using OAuth Authentication
If you have never worked with OAuth before then it might seem a little complex. But I will try to make it simple as far as possible. This is a two-step authentication process. How it works :
- Your code sends an OAuth request to Google, which has to be confirmed by an actual YouTube user. This is basically a login process where the user grants your code certain permissions that it has requested.
- On user confirmation, Google sends back an authorization token
- Your code then takes this authorization token and sends it back to Google in return for an access token.
- Once Google sends back an access token, the OAuth process is done and you can then use that access token in all your API calls
A Google OAuth access token has a validity of about 3600 seconds , which is an hour, from the time it was issued. If you use the access token beyond the expiry time, it will be rejected and your API call will fail.
So how to deal with the case where a YouTube user has granted your code access, and you want to run that code 3 days later? It would be a problem if the user has to again go through the process of granting access. For this purpose, Google sends back a refresh token along with the access token, but only if we ask for it in our request.
Once we have an access token for a user, we can use the refresh token to generate a new access token without user interaction, for subsequent API calls.
Here is the sample code for requesting OAuth access from a user:
<?php error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING ^ E_DEPRECATED); set_time_limit(60 * 3); session_start(); $clientId = "*****"; $clientSecret = "****"; $redirectURL = "http://test.dev/YouTubeapi/oauthresponse.php"; $url = "https://accounts.google.com/o/oauth2/v2/auth?client_id=" . $clientId . "&redirect_uri=" . urlencode($redirectURL) . "&response_type=code&scope=" . urlencode("https://www.googleapis.com/auth/YouTube") . "+" . urlencode("https://www.googleapis.com/auth/YouTube.upload"). "+" . urlencode("https://www.googleapis.com/auth/YouTubepartner") . "&access_type=offline&state=72663"; $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $url, CURLOPT_USERAGENT => 'YouTube API Tester', CURLOPT_SSL_VERIFYPEER => 1, CURLOPT_SSL_VERIFYHOST=> 0, CURLOPT_CAINFO => "cert/cacert.pem", CURLOPT_CAPATH => "cert/cacert.pem", CURLOPT_FOLLOWLOCATION => TRUE )); $resp = curl_exec($curl); curl_close($curl); exit($resp); ?>
The URL which is sent as an HTTP GET , consists of the following parts:
- client_id – the client id from Google API Console
- redirect_uri – this is the url to which Google will redirect back to after successful authentication. This must match the Authorized Redirect URL entered in the Google API Console
- response_type – always set to “code”
- scope – see possible values for this parameter in the Scope section below
- access_type – set to offline because we want to be able access the data even when the user is offline. The default value is “online”. The refresh token will only be generated if offline is specified.
- state – We are not using this parameter and are assigning any random string to it
Scope
The possible values for scope are shown below:
- https://www.googleapis.com/auth/YouTube – Manage your YouTube Account
- https://www.googleapis.com/auth/YouTube.force-ssl – Manage your YouTube account but only use SSL
- https://www.googleapis.com/auth/YouTube.readonly – Get readonly access to your YouTube account
- https://www.googleapis.com/auth/YouTube.upload – Ability to upload videos into your YouTube account
- https://www.googleapis.com/auth/YouTubepartner – Get access to your YouTube Partner account
- https://www.googleapis.com/auth/YouTubepartner-channel-audit – Get access to your YouTube Partner account with access to data for the audit process.
The output obtained from the curl call is the actual web page where Google shows the login for to the user. So we are dumping the contents of the response using exit($resp). This displays the Google page to the user and execution continues from there.
Here is how the output looks:
Screen 1
Screen 2
Screen 3
After clicking on allow, Google will redirect to the URL specified in the redirect_uri parameter. We have put oauthresponse.php as the redirect_uri . This is the sample code:
<?php error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING ^ E_DEPRECATED); session_start(); $g_YouTubeDataAPIKey = "***"; $clientId = "****"; $clientSecret = "****"; $redirectURL = "http://test.dev/YouTubeapi/oauthresponse.php"; // Google will redirect to this url if OAuth works if ($_GET["code"] != null && $_GET["code"] != "") { // we have auth code. now we need to send it to back to google to // get access tokens $url = "https://www.googleapis.com/oauth2/v4/token"; $data = array("client_id"=>$clientId,"client_secret"=>$clientSecret, "redirect_uri"=>$redirectURL, "code"=>$_GET["code"],"grant_type"=>"authorization_code", "response_type"=>"code"); $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $url, CURLOPT_USERAGENT => 'YouTube API Tester', CURLOPT_SSL_VERIFYPEER => 1, CURLOPT_SSL_VERIFYHOST=> 0, CURLOPT_CAINFO => "cert/cacert.pem", CURLOPT_CAPATH => "cert/cacert.pem", CURLOPT_FOLLOWLOCATION => TRUE, CURLOPT_POST=>true, CURLOPT_POSTFIELDS=>$postText = http_build_query($data) )); $resp = curl_exec($curl); curl_close($curl); var_dump($resp); ?>
The response obtained is a JSON object:
string(199) "{ "access_token": "ya29.GltOBBESC2L8rrthIjIV1XLKyz2LPgpSJ1eIE6ZsPtJNQVWAX3YdkKpO7qn_-gCd80wacuBGkTesudDodX1aoTeWYltmcwm6RvXn9TJLdmoc2gRdLHFuUoc6OpHz", "token_type": "Bearer", "expires_in": 3599, "refresh_token": "a29.GltOBBESC2L8rrthIjIV1XLKyz2LPgpSJ1eIE6ZsPtJNQVWAX3YdkKpO7qn_-gCd80wacuBGkTesudDodX1aoTeWYltmcwm6RvXn9TJLdmoc2gRdLHFu" }
which has four fields:
● access_token – initial access token generated
● token_type – always set to “Bearer”
● expires_on – expiry time in seconds
● refresh_token – Token to be used for subsequent API calls when access_token expires.
Once we have the access token, we can execute API calls which require OAuth. The sample code below lets you display the Activities in your own YouTube account. This only works if you have granted the code access rights via OAuth.
<?php error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING ^ E_DEPRECATED); set_time_limit(60 * 3); session_start(); $clientId = "****"; $clientSecret = "****"; $g_YouTubeDataAPIKey = "***"; $accessToken = "**" // make api request $url = "https://www.googleapis.com/YouTube/v3/activities?part=snippet,contentDetails,id&mine=true&maxResults=50&key=" . $g_YouTubeDataAPIKey . "&access_token=" . $accessToken; $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $url, CURLOPT_USERAGENT => 'YouTube API Tester', CURLOPT_SSL_VERIFYPEER => 1, CURLOPT_SSL_VERIFYHOST=> 0, CURLOPT_CAINFO => "../../cert/cacert.pem", CURLOPT_CAPATH => "../../cert/cacert.pem", CURLOPT_FOLLOWLOCATION => TRUE )); $resp = curl_exec($curl); curl_close($curl); var_dump($resp); ?>
We are not interested in the output of the code at the moment. Notice the access_token parameter passed to the API url. Also note that the API key has to be passed to the URL as well.
We have now seen that to work a YouTube API call which requires OAuth, we have to do a few things before we can actually get to the calling part. The OAuth process requires a two level communication with Google and the control is not linear – your code will start in one file and then get redirected to a different file .
Since this is something which we will need to do in several examples throughout the series and it will be cumbersome to repeat the same code for each and every example, what we need is a code flow which will work consistently for various sample files. The method I have outlined below is just one way of doing it and I have set it up so that it is easy to run independent sample files which are in this series. You might set it up differently as per your needs.
For our purposes we have two PHP files which will handle all OAuth processing:
- init-login.php – This will handle cases where
- User has to grant access for the first time
- User has already granted access and we can use a refresh_token
- The refresh_token fails to work due to expiry or other causes.
- oauthresponse.php – this is the file where Google redirects back to, when a user authenticates
There is a third php file called clear.php which clears the SESSION in case you want to start afresh. In case you get some weird OAuth errors, run clear.php first and then try again.
Apart from these two files we are using a simple text file in the subfolder data – data/response.txt where we persist the access data received from Google. Since the example files in this series will only be run one at a time, and there is just one user running it, a text file will do. For actual production or multi-user environments a database or a sophisticated membership layer will be the obvious choice.
This is how the flow works:
Code for init-login.php:
<?php error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING ^ E_DEPRECATED); set_time_limit(60 * 3); session_start(); $clientId = "**"; $clientSecret = "**"; $redirectURL = "http://test.dev/youtubeapi/oauthresponse.php"; // if response.txt exists then try to use access_token first , if that fails // then use refresh_token first $oauthFile = "data/response.txt"; $f = fopen($oauthFile, "r"); if ($f) { fclose($f); $contents = file_get_contents($oauthFile); $jsonData = unserialize($contents); $arrData = json_decode($jsonData, true); $accessToken = $arrData["access_token"]; $refreshToken = $arrData["refresh_token"]; $expiresIn = $arrData["expires_in"]; $tokenType = $arrData["token_type"]; // check if token has expired by comparing the TTL with file creation time // if access token can still be used then use it , else request a new one using // the refresh token $filetime = filemtime($oauthFile); $expiryTime = $filetime + $expiresIn; $currentTime = time(); if ($expiryTime > $currentTime) { $_SESSION["access_token"] = $accessToken; // redirect back to calling page if ($_SESSION["code_id"]) { header("Location:" . $_SESSION["code_id"]); exit; } } // if ($expiryTime > $currentTime) { if ($refreshToken != null && $refreshToken != "") { $url = "https://www.googleapis.com/oauth2/v4/token"; $data = array("client_id"=>$clientId,"client_secret"=>$clientSecret, "refresh_token"=>$refreshToken, "grant_type"=>"refresh_token"); $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $url, CURLOPT_USERAGENT => 'YouTube API Tester', CURLOPT_SSL_VERIFYPEER => 1, CURLOPT_SSL_VERIFYHOST=> 0, CURLOPT_CAINFO => "cert/cacert.pem", CURLOPT_CAPATH => "cert/cacert.pem", CURLOPT_FOLLOWLOCATION => TRUE, CURLOPT_POST=>true, CURLOPT_POSTFIELDS=>$postText = http_build_query($data) )); $resp = curl_exec($curl); curl_close($curl); $arrResp = json_decode($resp, true); $accessToken = $arrResp["access_token"]; $_SESSION["access_token"] = $accessToken; if ($accessToken == null) { var_dump($resp); exit; } // we have a new access token, so write it back to the file $arrData["access_token"] = $accessToken; $jsonData = json_encode($arrData); $f = fopen("data/response.txt", "w"); if ($f) { fwrite($f, serialize($jsonData)); fclose($f); } else exit("Error writing to response.txt"); // redirect back to calling page if ($_SESSION["code_id"]) { header("Location:" . $_SESSION["code_id"]); exit; } } // if ($refreshToken != null) { // if it reaches here it means the refresh_token failed to work so // run OAuth again } // if ($f) // do OAuth because either this is a first time request or the prev oauth data has expired $url = "https://accounts.google.com/o/oauth2/v2/auth?client_id=" . $clientId . "&redirect_uri=" . urlencode($redirectURL) . "&response_type=code&scope=" . urlencode("https://www.googleapis.com/auth/youtube") . "+" . urlencode("https://www.googleapis.com/auth/youtube.upload"). "+" . urlencode("https://www.googleapis.com/auth/youtubepartner") . "+" . urlencode("https://www.googleapis.com/auth/youtube.force-ssl") . "&access_type=offline&approval_prompt=force&state=72663"; $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $url, CURLOPT_USERAGENT => 'YouTube API Tester', CURLOPT_SSL_VERIFYPEER => 1, CURLOPT_SSL_VERIFYHOST=> 0, CURLOPT_CAINFO => "cert/cacert.pem", CURLOPT_CAPATH => "cert/cacert.pem", CURLOPT_FOLLOWLOCATION => TRUE )); $resp = curl_exec($curl); curl_close($curl); exit($resp); ?>
Code for oauthresponse.php
<?php error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING ^ E_DEPRECATED); session_start(); $clientId = "****"; $clientSecret = "***"; $redirectURL = "http://test.dev/YouTubeapi/oauthresponse.php"; // Google will redirect to this url if OAuth works if ($_GET["code"] != null && $_GET["code"] != "") { // we have auth code. now we need to send it to back to google to // get access tokens $url = "https://www.googleapis.com/oauth2/v4/token"; $data = array("client_id"=>$clientId,"client_secret"=>$clientSecret, "redirect_uri"=>$redirectURL, "code"=>$_GET["code"],"grant_type"=>"authorization_code", "response_type"=>"code"); $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $url, CURLOPT_USERAGENT => 'YouTube API Tester', CURLOPT_SSL_VERIFYPEER => 1, CURLOPT_SSL_VERIFYHOST=> 0, CURLOPT_CAINFO => "cert/cacert.pem", CURLOPT_CAPATH => "cert/cacert.pem", CURLOPT_FOLLOWLOCATION => TRUE, CURLOPT_POST=>true, CURLOPT_POSTFIELDS=>$postText = http_build_query($data) )); $resp = curl_exec($curl); curl_close($curl); $f = fopen("data/response.txt", "w"); if ($f) { fwrite($f, serialize($resp)); fclose($f); $arrResp = json_decode($resp, true); $accessToken = $arrResp["access_token"]; $_SESSION["access_token"] = $accessToken; } else exit("Error writing to response.txt"); if ($_SESSION["code_id"]) { header("Location:" . $_SESSION["code_id"]); exit; } } else { // if no code was returned then something failed //. Remove the response.txt from the folder @unlink("data/response.txt"); } ?>
The $_SESSION
[
"code_id"
]
is set in the calling php file. It just saves its current location, so that we can redirect back there when the OAuth process is complete.
How do I enter the redirect URI with Google? I am getting the error “Error: redirect_uri_mismatch” because I have not entered the redirect UTI anywhere in the API console. Can’t find it 🙁
Thanks for the article and advice.