Permalink
Browse files

YouTube: Add a YouTube Service definition.

  • Loading branch information...
beaulebens committed Feb 25, 2018
1 parent ca7a3fa commit 7072886bdf4b2dd8e43871688c2cde1ff9340049
Showing with 264 additions and 0 deletions.
  1. +264 −0 includes/services/extended/youtube.php
@@ -0,0 +1,264 @@
<?php
/**
* YouTube Service for Keyring.
*
* YouTube API: https://developers.google.com/youtube/v3/sample_requests
* OAuth implementation: https://developers.google.com/youtube/v3/guides/auth/server-side-web-apps
* App registration: https://console.developers.google.com/
*/
class Keyring_Service_YouTube extends Keyring_Service_OAuth2 {
const NAME = 'youtube';
const LABEL = 'YouTube';
const SCOPE = 'https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/userinfo.profile'; // See https://developers.google.com/youtube/v3/guides/auth/server-side-web-apps#identify-access-scopes
const ACCESS_TYPE = 'offline';
function __construct() {
parent::__construct();
// Enable "basic" UI for entering key/secret
if ( ! KEYRING__HEADLESS_MODE ) {
add_action( 'keyring_youtube_manage_ui', array( $this, 'basic_ui' ) );
add_filter( 'keyring_youtube_basic_ui_intro', array( $this, 'basic_ui_intro' ) );
}
$this->set_endpoint( 'authorize', 'https://accounts.google.com/o/oauth2/v2/auth', 'GET' );
$this->set_endpoint( 'access_token', 'https://www.googleapis.com/oauth2/v4/token', 'POST' );
$this->set_endpoint( 'refresh', 'https://www.googleapis.com/oauth2/v4/token', 'POST' );
$this->set_endpoint( 'userinfo', 'https://www.googleapis.com/oauth2/v3/userinfo', 'GET' );
$creds = $this->get_credentials();
$this->redirect_uri = $creds['redirect_uri'];
$this->key = $creds['key'];
$this->secret = $creds['secret'];
$this->authorization_header = 'Bearer';
$this->authorization_parameter = false;
// Need to reset the callback because Google is very strict about where it sends people
if ( ! empty( $creds['redirect_uri'] ) ) {
$this->callback_url = $creds['redirect_uri']; // Allow user to manually enter a redirect URI
} else {
$this->callback_url = remove_query_arg( array( 'nonce', 'kr_nonce' ), $this->callback_url ); // At least strip nonces, since you can't save them in your app config
}
add_filter( 'keyring_youtube_request_token_params', array( $this, 'request_token_params' ) );
add_action( 'pre_keyring_youtube_verify', array( $this, 'redirect_incoming_verify' ) );
}
function basic_ui_intro() {
echo '<p>' . sprintf( __( "Google controls access to all of their APIs through their API Console. <a href='%s'>Go to the Library page in the console</a> and click the <strong>Select a project</strong> dropdown next to the logo in the upper left of the screen. Click the <strong>plus icon</strong> to create a new project. Enter a name and then click <strong>Create</strong>.", 'keyring' ), 'https://console.developers.google.com/apis/library' ) . '</p>';
echo '<p>' . __( "Now you need to enable the YouTube API and setup your OAuth credentials.", 'keyring' );
echo '<ol>';
echo '<li>' . __( "Select your project from the project dropdown.", 'keyring' ) . '</li>';
echo '<li>' . __( "Click <strong>Library</strong> in the menu on the left.", 'keyring' ) . '</li>';
echo '<li>' . __( "Find and click <strong>YouTube API</strong>.", 'keyring' ) . '</li>';
echo '<li>' . __( "Next to the heading, click <strong>Enable</strong>.", 'keyring' ) . '</li>';
echo '<li>' . __( "Click the blue button labelled <strong>Create credentials</strong>.", 'keyring' ) . '</li>';
echo '<li>' . __( "Click <strong>Credential</strong> in the menu on the left.", 'keyring' ) . '</li>';
echo '<li>' . __( "Click the <strong>OAuth consent screen</strong> menu item.", 'keyring' ) . '</li>';
echo '<li>' . __( "You must enter a <strong>Product name</strong>, but you can skip the logo and home page URL.", 'keyring' ) . '</li>';
echo '<li>' . __( "Click Save.", 'keyring' ) . '</li>';
echo '<li>' . __( "Click the <strong>Create credentials</strong> button and select <strong>OAuth client ID</strong>.", 'keyring' ) . '</li>';
echo '<li>' . __( "Select <strong>Web application</strong> and enter a relevant name or just use the default.", 'keyring' ) . '</li>';
echo '<li>' . sprintf( __( "For the <strong>Authorized JavaScript Origins</strong>, enter the URL of your domain, e.g. <code>http://%s</code>.", 'keyring' ), $_SERVER['HTTP_HOST'] ) . '</li>';
echo '<li>' . sprintf( __( "In the <strong>Authorized Redirect URIs</strong> box, enter the URL <code>%s</code>.", 'keyring' ), Keyring_Util::admin_url( $this->get_name(), array( 'action' => 'verify' ) ) ) . '</li>';
echo '<li>' . __( "Click <strong>Create</strong> when you're done.", 'keyring' ) . '</li>';
echo '</ol>';
echo '<p>' . __( "Once you've saved your details, copy the <strong>Client ID</strong> into the <strong>Client ID</strong> field below, and the <strong>Client secret</strong> value into <strong>Client Secret</strong>. The Redirect URI box should fill itself out for you.", 'keyring' ) . '</p>';
}
function _get_credentials() {
if (
defined( 'KEYRING__YOUTUBE_KEY' )
&&
defined( 'KEYRING__YOUTUBE_SECRET' )
) {
return array(
'redirect_uri' => defined( 'KEYRING__YOUTUBE_URI' ) ? constant( 'KEYRING__YOUTUBE_URI' ) : '', // optional
'key' => constant( 'KEYRING__YOUTUBE_KEY' ),
'secret' => constant( 'KEYRING__YOUTUBE_SECRET' ),
);
} else {
return null;
}
}
function request_token_params( $params ) {
$params['scope'] = self::SCOPE;
$params['access_type'] = self::ACCESS_TYPE;
$params['prompt'] = 'consent'; // Required to get a refresh token
return $params;
}
function redirect_incoming_verify( $request ) {
if ( ! isset( $request['kr_nonce'] ) ) {
$kr_nonce = wp_create_nonce( 'keyring-verify' );
$nonce = wp_create_nonce( 'keyring-verify-' . $this->get_name() );
wp_safe_redirect(
Keyring_Util::admin_url(
$this->get_name(),
array(
'action' => 'verify',
'kr_nonce' => $kr_nonce,
'nonce' => $nonce,
'state' => $request['state'],
'code' => $request['code'], // Auth code from successful response (maybe)
)
)
);
exit;
}
}
function build_token_meta( $token ) {
$meta = array(
'refresh_token' => $token['refresh_token'],
'expires' => time() + $token['expires_in'],
);
$this->set_token(
new Keyring_Access_Token(
$this->get_name(),
$token['access_token'],
array()
)
);
$response = $this->request( $this->userinfo_url, array( 'method' => $this->userinfo_method ) );
if ( ! Keyring_Util::is_error( $response ) ) {
$meta['user_id'] = $response->sub;
$meta['name'] = $response->name;
$meta['picture'] = $response->picture;
}
return apply_filters( 'keyring_access_token_meta', $meta, $this->get_name(), $token, array(), $this );
}
function get_display( Keyring_Access_Token $token ) {
return $token->get_meta( 'name' );
}
function maybe_refresh_token() {
// Request a new token, using the refresh_token
$token = $this->get_token();
$meta = $token->get_meta();
if ( empty( $meta['refresh_token'] ) ) {
return false;
}
// Don't refresh if token is valid
if ( ! $token->is_expired( 20 ) ) {
return;
}
$response = wp_remote_post( $this->refresh_url, array(
'method' => $this->refresh_method,
'body' => array(
'client_id' => $this->key,
'client_secret' => $this->secret,
'refresh_token' => $meta['refresh_token'],
'grant_type' => 'refresh_token',
),
) );
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
return false;
}
$return = json_decode( wp_remote_retrieve_body( $response ) );
$meta['expires'] = time() + $return->expires_in;
// Build access token
$access_token = new Keyring_Access_Token(
$this->get_name(),
$return->access_token,
$meta,
$this->token->unique_id
);
// Store the updated access token
$access_token = apply_filters( 'keyring_access_token', $access_token, $token );
$id = $this->store->update( $access_token );
// And switch to using it
$this->set_token( $access_token );
}
// Need to potentially refresh token before each request
function request( $url, array $params = array() ) {
$this->maybe_refresh_token();
return parent::request( $url, $params );
}
// Minor modifications from Keyring_Service::basic_ui
function basic_ui() {
if ( ! isset( $_REQUEST['nonce'] ) || ! wp_verify_nonce( $_REQUEST['nonce'], 'keyring-manage-' . $this->get_name() ) ) {
Keyring::error( __( 'Invalid/missing management nonce.', 'keyring' ) );
exit;
}
// Common Header
echo '<div class="wrap">';
echo '<h2>' . __( 'Keyring Service Management', 'keyring' ) . '</h2>';
echo '<p><a href="' . Keyring_Util::admin_url( false, array( 'action' => 'services' ) ) . '">' . __( '&larr; Back', 'keyring' ) . '</a></p>';
echo '<h3>' . sprintf( __( '%s API Credentials', 'keyring' ), esc_html( $this->get_label() ) ) . '</h3>';
// Handle actually saving credentials
if ( isset( $_POST['api_key'] ) && isset( $_POST['api_secret'] ) ) {
// Store credentials against this service
$this->update_credentials( array(
'key' => stripslashes( $_POST['api_key'] ),
'secret' => stripslashes( $_POST['api_secret'] ),
'redirect_uri' => stripslashes( $_POST['redirect_uri'] ),
) );
echo '<div class="updated"><p>' . __( 'Credentials saved.', 'keyring' ) . '</p></div>';
}
$api_key = $api_secret = $redirect_uri = '';
if ( $creds = $this->get_credentials() ) {
$api_key = $creds['key'];
$api_secret = $creds['secret'];
$redirect_uri = $creds['redirect_uri'];
}
echo apply_filters( 'keyring_' . $this->get_name() . '_basic_ui_intro', '' );
if ( ! $redirect_uri ) {
$redirect_uri = Keyring_Util::admin_url( $this->get_name(), array( 'action' => 'verify' ) );
}
// Output basic form for collecting key/secret
echo '<form method="post" action="">';
echo '<input type="hidden" name="service" value="' . esc_attr( $this->get_name() ) . '" />';
echo '<input type="hidden" name="action" value="manage" />';
wp_nonce_field( 'keyring-manage', 'kr_nonce', false );
wp_nonce_field( 'keyring-manage-' . $this->get_name(), 'nonce', false );
echo '<table class="form-table">';
echo '<tr><th scope="row">' . __( 'Client ID', 'keyring' ) . '</th>';
echo '<td><input type="text" name="api_key" value="' . esc_attr( $api_key ) . '" id="api_key" class="regular-text"></td></tr>';
echo '<tr><th scope="row">' . __( 'Client Secret', 'keyring' ) . '</th>';
echo '<td><input type="text" name="api_secret" value="' . esc_attr( $api_secret ) . '" id="api_secret" class="regular-text"></td></tr>';
echo '<tr><th scope="row">' . __( 'Redirect URI', 'keyring' ) . '</th>';
echo '<td><input type="text" name="redirect_uri" value="' . esc_attr( $redirect_uri ) . '" id="redirect_uri" class="regular-text"></td></tr>';
echo '</table>';
echo '<p class="submitbox">';
echo '<input type="submit" name="submit" value="' . __( 'Save Changes', 'keyring' ) . '" id="submit" class="button-primary">';
echo '<a href="' . esc_url( $_SERVER['HTTP_REFERER'] ) . '" class="submitdelete" style="margin-left:2em;">' . __( 'Cancel', 'keyring' ) . '</a>';
echo '</p>';
echo '</form>';
echo '</div>';
}
function test_connection() {
$res = $this->request( $this->userinfo_url, array( 'method' => $this->userinfo_method ) );
if ( ! Keyring_Util::is_error( $res ) ) {
return true;
}
return $res;
}
}
add_action( 'keyring_load_services', array( 'Keyring_Service_YouTube', 'init' ) );

0 comments on commit 7072886

Please sign in to comment.