Search code examples
google-apps-scriptoauth-2.0google-apiyoutube-api

Add Redirect URI To Automatically Generated Google OAuth 2.0 Client ID


I'm trying to make a Google sheet that integrates with the YouTube Data and Analytics API. However, when trying to implement this, I came into a known issue with regards to allowing brand YouTube accounts/channels to be authenticated with a Google app which is explained here. https://issuetracker.google.com/issues/36764531

To get around this, the document mentions instructions from this link which I am now trying to implement myself https://mashe.hawksey.info/2017/09/identity-crisis-using-the-youtube-api-with-google-apps-script-and-scheduling-live-broadcasts-from-google-sheets/

From the instructions, I have:

  1. Imported the necessary libraries into the script
  2. Added the necessary Google Apps Script code which is at the bottom
  3. Created my own OAuth 2.0 Client ID credentials in the Google Cloud Console

However, in the first link, a comment had also been added to say that while the instructions still worked, the script project now has to be associated with a cloud project and so that's what I did. As part of this, it created its own OAuth 2.0 Client ID which I believe it's now using rather than the credentials I had already generated myself. I have added a picture below to illustrate what I mean. I also then can't edit these new credentials meaning that I can't add any redirect URI.

enter image description here

Is there a way I can add the redirect URI to the automatically generated credentials? The problem now is that if I then run the setup function from the script as per the instructions, when I then try to open this link it gives me, I then get given the following message

Error 400: redirect_uri_mismatch

The redirect URI in the request, https://script.google.com/macros/d/12u2laknmO_9-zgxBbAX6wG9gJDUOvgJmYm5UquJsamShus9s5McrGBar/usercallback, does not match the ones authorized for the OAuth client. To update the authorized redirect URIs, visit: https://console.developers.google.com/apis/credentials/oauthclient/${your_client_id}?project=${your_project_number}

/**
 * Authorizes and makes a request to the YouTube Data API.
 */
function setup() {
  var service = getYouTubeService();
  YouTube.setTokenService(function(){ return service.getAccessToken(); });
  if (service.hasAccess()) {
    var result = YouTube.channelsList("snippet", {mine:true});
    Logger.log(JSON.stringify(result, null, 2));
    throw "Open View > Logs to see result";
  } else {
    var authorizationUrl = service.getAuthorizationUrl();
    Logger.log('Open the following URL and re-run the script: %s',
        authorizationUrl);
    throw "Open View > Logs to get authentication url";
  }
}
 
 
/**
 * Configures the service.
 */
function getYouTubeService() {
  return OAuth2.createService('YouTube')
      // Set the endpoint URLs.
      .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
      .setTokenUrl('https://accounts.google.com/o/oauth2/token')
 
      // Set the client ID and secret.
      .setClientId(getStaticScriptProperty_('client_id'))
      .setClientSecret(getStaticScriptProperty_('client_secret'))
 
      // Set the name of the callback function that should be invoked to complete
      // the OAuth flow.
      .setCallbackFunction('authCallback')
 
      // Set the property store where authorized tokens should be persisted
      // you might want to switch to Script Properties if sharing access
      .setPropertyStore(PropertiesService.getUserProperties())
 
      // Set the scope and additional Google-specific parameters.
      .setScope(["https://www.googleapis.com/auth/youtube",
      "https://www.googleapis.com/auth/youtube.force-ssl",
      "https://www.googleapis.com/auth/youtube.readonly",
      "https://www.googleapis.com/auth/youtubepartner",
      "https://www.googleapis.com/auth/youtubepartner-channel-audit"])
      .setParam('access_type', 'offline');
}
 
/**
 * Handles the OAuth callback.
 */
function authCallback(request) {
  var service = getYouTubeService();
  var authorized = service.handleCallback(request);
  if (authorized) {
    return HtmlService.createHtmlOutput('Success!');
  } else {
    return HtmlService.createHtmlOutput('Denied');
  }
}
 
/**
 * Logs the redirect URI to register in the Google Developers Console.
 */
function logRedirectUri() {
  var service = getYouTubeService();
  Logger.log(service.getRedirectUri());
  throw "Open View > Logs to get redirect url";
}
 
/**
 * Reset the authorization state, so that it can be re-tested.
 */
function reset() {
  var service = getYouTubeService();
  service.reset();
}
 
/**
 * Gets a static script property, using long term caching.
 * @param {string} key The property key.
 * @returns {string} The property value.
 */
function getStaticScriptProperty_(key) {
  var value = CacheService.getScriptCache().get(key);
  if (!value) {
    value = PropertiesService.getScriptProperties().getProperty(key);
    CacheService.getScriptCache().put(key, value, 21600);
  }
  return value;
}

I hope this is clear but if not then I can answer any further questions. Or if there is a better way of being able to authenticate a brand YouTube account with the API, then please let me know.


Solution

  • In the end I had to make new credentials in the console. Once I had done this and bound it with the script, it then seemed to work as expected.