Google Workspace के ऐड-ऑन को तीसरे पक्ष की सेवा से कनेक्ट करना

लिंक की झलक दिखाने वाले कस्टम ऑथराइज़ेशन कार्ड में कंपनी का लोगो, ब्यौरा, और साइन इन बटन शामिल है.
यह ऐड-ऑन के लिए साइन-इन कार्ड इंटरफ़ेस है. यह तीसरे पक्ष की सेवा से मिले लिंक की झलक दिखाता है.

अगर आपका Google Workspace ऐड-ऑन, तीसरे पक्ष की किसी ऐसी सेवा या एपीआई से कनेक्ट होता है जिसके लिए अनुमति ज़रूरी है, तो ऐड-ऑन लोगों को साइन इन करने और ऐक्सेस करने की अनुमति देने के लिए कह सकता है.

इस पेज पर, ऑथराइज़ेशन फ़्लो (जैसे कि OAuth) का इस्तेमाल करके उपयोगकर्ताओं की पुष्टि करने का तरीका बताया गया है. इसमें ये चरण शामिल हैं:

  1. यह कुकी यह पता लगाती है कि अनुमति कब ज़रूरी है.
  2. ऐसा कार्ड इंटरफ़ेस दिखाएं जो लोगों को सेवा में साइन इन करने के लिए कहे.
  3. ऐड-ऑन को रीफ़्रेश करें, ताकि उपयोगकर्ता सेवा या सुरक्षित संसाधन को ऐक्सेस कर सकें.

अगर आपके ऐड-ऑन को सिर्फ़ उपयोगकर्ता की पहचान की ज़रूरत है, तो उपयोगकर्ताओं की पुष्टि सीधे तौर पर की जा सकती है. इसके लिए, उनके Google Workspace आईडी या ईमेल पते का इस्तेमाल करें. पुष्टि करने के लिए ईमेल पते का इस्तेमाल करने के बारे में जानने के लिए, JSON अनुरोधों की पुष्टि करना लेख पढ़ें. अगर आपने Google Apps Script का इस्तेमाल करके ऐड-ऑन बनाया है, तो OAuth2 for Google Apps Script लाइब्रेरी का इस्तेमाल करके, इस प्रोसेस को आसान बनाया जा सकता है. इसका OAuth1 वर्शन भी उपलब्ध है.

यह पता लगाना कि अनुमति ज़रूरी है

ऐड-ऑन का इस्तेमाल करते समय, ऐसा हो सकता है कि उपयोगकर्ताओं के पास सुरक्षित संसाधन को ऐक्सेस करने की अनुमति न हो. इसकी कई वजहें हो सकती हैं. जैसे:

  • तीसरे पक्ष की सेवा से कनेक्ट करने के लिए, अब तक कोई ऐक्सेस टोकन जनरेट नहीं किया गया है या वह खत्म हो गया है.
  • ऐक्सेस टोकन में, अनुरोध किया गया संसाधन शामिल नहीं है.
  • ऐक्सेस टोकन में, अनुरोध के लिए ज़रूरी स्कोप शामिल नहीं हैं.

आपके ऐड-ऑन को इन मामलों का पता लगाना चाहिए, ताकि उपयोगकर्ता साइन इन कर सकें और आपकी सेवा को ऐक्सेस कर सकें.

अगर Apps Script में कोई ऐप्लिकेशन बनाया जा रहा है, तो OAuth लाइब्रेरी का hasAccess() फ़ंक्शन आपको यह बता सकता है कि आपके पास किसी सेवा का ऐक्सेस है या नहीं. इसके अलावा, UrlFetchApp fetch() अनुरोधों का इस्तेमाल करते समय, muteHttpExceptions पैरामीटर को true पर सेट किया जा सकता है. इससे अनुरोध पूरा न होने पर, अनुरोध में कोई गड़बड़ी नहीं होती. साथ ही, इससे आपको HttpResponse ऑब्जेक्ट में, अनुरोध के जवाब का कोड और कॉन्टेंट देखने में मदद मिलती है.

लोगों को आपकी सेवा में साइन इन करने के लिए प्रॉम्प्ट करना

जब आपके ऐड-ऑन को पता चलता है कि अनुमति ज़रूरी है, तो ऐड-ऑन को कार्ड इंटरफ़ेस दिखाना होगा, ताकि उपयोगकर्ताओं को सेवा में साइन इन करने के लिए कहा जा सके. साइन-इन कार्ड को उपयोगकर्ताओं को रीडायरेक्ट करना होगा, ताकि वे तीसरे पक्ष की पुष्टि करने और अनुमति देने की प्रोसेस को आपके इन्फ़्रास्ट्रक्चर पर पूरा कर सकें.

एचटीटीपी एंडपॉइंट का इस्तेमाल करके ऐड-ऑन बनाते समय, हम आपको यह सुझाव देते हैं कि डेस्टिनेशन ऐप्लिकेशन को Google साइन-इन से सुरक्षित रखें. साथ ही, साइन-इन के दौरान जारी किए गए पहचान टोकन का इस्तेमाल करके उपयोगकर्ता का आईडी पाएं. सब-क्लेम में उपयोगकर्ता का यूनीक आईडी होता है. इसे आपके ऐड-ऑन के आईडी से जोड़ा जा सकता है.

साइन-इन कार्ड बनाना और उसे वापस भेजना

अपनी सेवा के साइन-इन कार्ड के लिए, Google के बुनियादी पुष्टि वाले कार्ड का इस्तेमाल किया जा सकता है. इसके अलावा, कार्ड को अपनी पसंद के मुताबिक बनाया जा सकता है, ताकि अतिरिक्त जानकारी दिखाई जा सके. जैसे, आपके संगठन का लोगो. अगर आपको अपने ऐड-ऑन को सार्वजनिक तौर पर पब्लिश करना है, तो आपको कस्टम कार्ड का इस्तेमाल करना होगा.

बुनियादी अनुमति वाला कार्ड

इस इमेज में, Google के बुनियादी पुष्टि वाले कार्ड का उदाहरण दिखाया गया है:

उदाहरण के तौर पर दिए गए खाते के लिए, अनुमति देने का बुनियादी प्रॉम्प्ट. प्रॉम्प्ट में बताया गया है कि ऐड-ऑन को अतिरिक्त जानकारी दिखानी है. हालांकि, इसके लिए उसे उपयोगकर्ता से खाते को ऐक्सेस करने की अनुमति चाहिए.

उपयोगकर्ताओं को बुनियादी ऑथराइज़ेशन कार्ड दिखाने के लिए, आपको ऑब्जेक्ट AuthorizationError दिखाना होगा. यहां दिए गए कोड में, AuthorizationError ऑब्जेक्ट का उदाहरण दिखाया गया है:

Apps Script

CardService.newAuthorizationException()
    .setAuthorizationUrl('AUTHORIZATION_URL')
    .setResourceDisplayName('RESOURCE_DISPLAY_NAME')
    .throwException();

JSON

यह JSON रिस्पॉन्स दिखाएं:

{
  "basic_authorization_prompt": {
    "authorization_url": "AUTHORIZATION_URL",
    "resource": "RESOURCE_DISPLAY_NAME"
  }
}

इनकी जगह ये डालें:

  • AUTHORIZATION_URL: यह अनुमति देने वाले वेब ऐप्लिकेशन का यूआरएल होता है.
  • RESOURCE_DISPLAY_NAME: सुरक्षित किए गए संसाधन या सेवा का डिसप्ले नेम. यह नाम, उपयोगकर्ता को अनुमति देने के लिए दिखने वाले प्रॉम्प्ट पर दिखता है. उदाहरण के लिए, अगर आपका RESOURCE_DISPLAY_NAME Example Account है, तो प्रॉम्प्ट में यह मैसेज दिखेगा: "यह ऐड-ऑन अतिरिक्त जानकारी दिखाना चाहेगा, लेकिन इसे आपका Example Account ऐक्सेस करने की अनुमति चाहिए."

अनुमति देने की प्रोसेस पूरी होने के बाद, उपयोगकर्ता को सुरक्षित किए गए संसाधन को ऐक्सेस करने के लिए, ऐड-ऑन को रीफ़्रेश करने के लिए कहा जाता है.

Google Chat में, सामान वापस भेजने की अनुमति देने वाले कार्ड दिखाना

अगर आपका ऐड-ऑन Google Chat की सुविधाओं को बढ़ाता है और उपयोगकर्ता इसे Google Chat में इस्तेमाल करता है, तो वह मैन्युअल तरीके से रीफ़्रेश किए बिना, अनुमति देने की प्रोसेस पूरी कर सकता है. अगर ट्रिगर मैसेज, स्पेस में जोड़ा गया या ऐप्लिकेशन कमांड है, तो Google Chat पिछली कार्रवाई को अपने-आप फिर से करने की सुविधा देता है. इन ट्रिगर के लिए, आपके ऐड-ऑन को इवेंट पेलोड में completeRedirectUri मिलता है. अपने कॉन्फ़िगरेशन यूआरएल में completeRedirectUri को एन्कोड करना ज़रूरी है, ताकि अपने-आप फिर से कोशिश करने की सुविधा ट्रिगर हो सके. इस यूआरएल पर रीडायरेक्ट करने से Google Chat को यह सिग्नल मिलता है कि कॉन्फ़िगरेशन का अनुरोध पूरा हो गया है. इससे Google Chat को पिछली कार्रवाई को फिर से करने की अनुमति मिलती है.

जब किसी उपयोगकर्ता को ओरिजनल मैसेज में दिए गए configCompleteRedirectUrl पर रीडायरेक्ट किया जाता है, तो Google Chat ये कार्रवाइयां करता है:

  1. यह कुकी, प्रॉम्प्ट को मिटा देती है. यह प्रॉम्प्ट, ऐप्लिकेशन इस्तेमाल करना शुरू करने वाले व्यक्ति को दिखता है.
  2. यह मूल इवेंट ऑब्जेक्ट को उसी ऐड-ऑन को दूसरी बार भेजता है.

अगर कॉन्फ़िगरेशन यूआरएल में completeRedirectUri को एन्कोड नहीं किया जाता है, तो भी उपयोगकर्ता ऑथराइज़ेशन फ़्लो पूरा कर सकता है. हालांकि, Google Chat पिछली बार के अनुरोध को फिर से नहीं भेजता है. इसलिए, उपयोगकर्ता को ऐड-ऑन को मैन्युअल तरीके से फिर से चालू करना होगा.

यहां दिए गए कोड के उदाहरण में बताया गया है कि Chat ऐप्लिकेशन, ऑफ़लाइन OAuth2 क्रेडेंशियल का अनुरोध कैसे कर सकता है. साथ ही, उन्हें डेटाबेस में सेव करके, उपयोगकर्ता की पुष्टि के साथ एपीआई कॉल करने के लिए उनका इस्तेमाल कैसे कर सकता है.

कस्टम ऑथराइज़ेशन कार्ड

अनुमति देने के लिए दिखने वाले प्रॉम्प्ट में बदलाव करने के लिए, अपनी सेवा के साइन-इन अनुभव के लिए कस्टम कार्ड बनाया जा सकता है.

अगर आपको अपना ऐड-ऑन सार्वजनिक तौर पर पब्लिश करना है, तो आपको Chat को छोड़कर, सभी Google Workspace होस्ट ऐप्लिकेशन के लिए कस्टम ऑथराइज़ेशन कार्ड का इस्तेमाल करना होगा. Google Workspace Marketplace पर ऐप्लिकेशन पब्लिश करने से जुड़ी ज़रूरी शर्तों के बारे में ज़्यादा जानने के लिए, ऐप्लिकेशन की समीक्षा के बारे में जानकारी लेख पढ़ें.

वापस किए गए कार्ड में ये चीज़ें होनी चाहिए:

  • उपयोगकर्ता को साफ़ तौर पर बताएं कि ऐड-ऑन, उसकी ओर से Google से बाहर की किसी सेवा को ऐक्सेस करने की अनुमति मांग रहा है.
  • यह साफ़ तौर पर बताना कि अनुमति मिलने पर ऐड-ऑन क्या-क्या कर सकता है.
  • उसमें एक बटन या ऐसा ही कोई विजेट होना चाहिए, जो उपयोगकर्ता को सेवा के अनुमति वाले यूआरएल पर ले जाए. पक्का करें कि उपयोगकर्ता को इस विजेट के फ़ंक्शन के बारे में साफ़ तौर पर पता हो.
  • ऊपर दिए गए विज़ेट को अपने OpenLink ऑब्जेक्ट में OnClose.RELOAD सेटिंग का इस्तेमाल करना चाहिए, ताकि यह पक्का किया जा सके कि अनुमति मिलने के बाद ऐड-ऑन फिर से लोड हो जाए.
  • अनुमति देने के लिए दिखने वाले प्रॉम्प्ट से खोले गए सभी लिंक में एचटीटीपीएस का इस्तेमाल होना चाहिए.

यहां दी गई इमेज में, ऐड-ऑन के होम पेज के लिए कस्टम ऑथराइज़ेशन कार्ड का उदाहरण दिखाया गया है. कार्ड में लोगो, ब्यौरा, और साइन-इन बटन शामिल होता है:

Cymbal Labs के लिए कस्टम ऑथराइज़ेशन कार्ड. इसमें कंपनी का लोगो, ब्यौरा, और साइन इन बटन शामिल है.

यहां दिए गए कोड में, कस्टम कार्ड के इस उदाहरण को इस्तेमाल करने का तरीका बताया गया है:

Apps Script

function customAuthorizationCard() {
    let cardSection1Image1 = CardService.newImage()
        .setImageUrl('LOGO_URL')
        .setAltText('LOGO_ALT_TEXT');

    let cardSection1Divider1 = CardService.newDivider();

    let cardSection1TextParagraph1 = CardService.newTextParagraph()
        .setText('DESCRIPTION');

    let cardSection1ButtonList1Button1 = CardService.newTextButton()
        .setText('Sign in')
        .setBackgroundColor('#0055ff')
        .setTextButtonStyle(CardService.TextButtonStyle.FILLED)
        .setAuthorizationAction(CardService.newAuthorizationAction()
            .setAuthorizationUrl('AUTHORIZATION_URL'));

    let cardSection1ButtonList1 = CardService.newButtonSet()
        .addButton(cardSection1ButtonList1Button1);

    let cardSection1TextParagraph2 = CardService.newTextParagraph()
        .setText('TEXT_SIGN_UP');

    let cardSection1 = CardService.newCardSection()
        .addWidget(cardSection1Image1)
        .addWidget(cardSection1Divider1)
        .addWidget(cardSection1TextParagraph1)
        .addWidget(cardSection1ButtonList1)
        .addWidget(cardSection1TextParagraph2);

    let card = CardService.newCardBuilder()
        .addSection(cardSection1)
        .build();
    return [card];
}

function startNonGoogleAuth() {
    CardService.newAuthorizationException()
        .setAuthorizationUrl('AUTHORIZATION_URL')
        .setResourceDisplayName('RESOURCE_DISPLAY_NAME')
        .setCustomUiCallback('customAuthorizationCard')
        .throwException();
  }

JSON

यह JSON रिस्पॉन्स दिखाएं:

{
  "custom_authorization_prompt": {
    "action": {
      "navigations": [
        {
          "pushCard": {
            "sections": [
              {
                "widgets": [
                  {
                    "image": {
                      "imageUrl": "LOGO_URL",
                      "altText": "LOGO_ALT_TEXT"
                    }
                  },
                  {
                    "divider": {}
                  },
                  {
                    "textParagraph": {
                      "text": "DESCRIPTION"
                    }
                  },
                  {
                    "buttonList": {
                      "buttons": [
                        {
                          "text": "Sign in",
                          "onClick": {
                            "openLink": {
                              "url": "AUTHORIZATION_URL",
                              "onClose": "RELOAD",
                              "openAs": "OVERLAY"
                            }
                          },
                          "color": {
                            "red": 0,
                            "green": 0,
                            "blue": 1,
                            "alpha": 1,
                          }
                        }
                      ]
                    }
                  },
                  {
                    "textParagraph": {
                      "text": "TEXT_SIGN_UP"
                    }
                  }
                ]
              }
            ]
          }
        }
      ]
    }
  }
}

इनकी जगह ये डालें:

  • LOGO_URL: यह लोगो या इमेज का यूआरएल होता है. यह एक सार्वजनिक यूआरएल होना चाहिए.
  • LOGO_ALT_TEXT: लोगो या इमेज के लिए वैकल्पिक टेक्स्ट, जैसे कि Cymbal Labs Logo.
  • DESCRIPTION: उपयोगकर्ताओं के लिए साइन इन करने का कॉल टू ऐक्शन, जैसे कि Sign in to get started.
  • साइन इन बटन को अपडेट करने के लिए:
    • AUTHORIZATION_URL: यह अनुमति देने वाले वेब ऐप्लिकेशन का यूआरएल होता है.
    • ज़रूरी नहीं: बटन का रंग बदलने के लिए, color फ़ील्ड की आरजीबीए फ़्लोट वैल्यू अपडेट करें. Apps Script के लिए, हेक्साडेसिमल वैल्यू का इस्तेमाल करके setBackgroundColor() तरीके को अपडेट करें.
  • TEXT_SIGN_UP: ऐसा टेक्स्ट जो उपयोगकर्ताओं को खाता बनाने के लिए कहे, अगर उनके पास खाता नहीं है. उदाहरण के लिए, New to Cymbal Labs? <a href=\"https://www.example.com/signup\">Sign up</a> here.

Google Workspace के सभी ऐप्लिकेशन में, तीसरे पक्ष की मदद से लॉगिन करने की सुविधा मैनेज करना

Google Workspace ऐड-ऑन का एक सामान्य इस्तेमाल यह है कि Google Workspace के होस्ट ऐप्लिकेशन में, तीसरे पक्ष के सिस्टम के साथ इंटरैक्ट करने के लिए इंटरफ़ेस उपलब्ध कराया जाए.

तीसरे पक्ष के सिस्टम में, उपयोगकर्ता को अक्सर उपयोगकर्ता आईडी, पासवर्ड या अन्य क्रेडेंशियल का इस्तेमाल करके साइन इन करना होता है. जब कोई उपयोगकर्ता Google Workspace होस्ट का इस्तेमाल करते समय, आपकी तीसरे पक्ष की सेवा में साइन इन करता है, तो आपको यह पक्का करना होगा कि जब वह किसी दूसरे Google Workspace होस्ट पर स्विच करे, तो उसे फिर से साइन इन न करना पड़े.

अगर Apps Script में कोई ऐप्लिकेशन बनाया जा रहा है, तो उपयोगकर्ता प्रॉपर्टी या आईडी टोकन की मदद से, बार-बार लॉगिन करने के अनुरोधों को रोका जा सकता है. इनके बारे में यहां बताया गया है.

उपयोगकर्ता प्रॉपर्टी

Apps Script की उपयोगकर्ता प्रॉपर्टी में, किसी उपयोगकर्ता के साइन-इन डेटा को सेव किया जा सकता है. उदाहरण के लिए, उनकी लॉगिन सेवा से अपना JSON Web Token (JWT) बनाया जा सकता है. साथ ही, उसे उपयोगकर्ता प्रॉपर्टी में रिकॉर्ड किया जा सकता है. इसके अलावा, उनकी सेवा के लिए उपयोगकर्ता नाम और पासवर्ड रिकॉर्ड किया जा सकता है.

उपयोगकर्ता प्रॉपर्टी का दायरा इस तरह से तय किया जाता है कि वे सिर्फ़ उस उपयोगकर्ता के लिए उपलब्ध हों. साथ ही, वे आपके ऐड-ऑन की स्क्रिप्ट में ही उपलब्ध हों. अन्य उपयोगकर्ता और अन्य स्क्रिप्ट इन प्रॉपर्टी को ऐक्सेस नहीं कर सकतीं. ज़्यादा जानकारी के लिए, PropertiesService देखें.

आईडी टोकन

अपनी सेवा के लिए लॉगिन क्रेडेंशियल के तौर पर, Google आईडी टोकन का इस्तेमाल किया जा सकता है. यह सिंगल साइन-ऑन की सुविधा पाने का एक तरीका है. उपयोगकर्ताओं ने पहले से ही Google में लॉग इन किया हुआ है, क्योंकि वे Google के होस्ट ऐप्लिकेशन में हैं.

Google के बाहर के OAuth कॉन्फ़िगरेशन का उदाहरण

नीचे दिए गए Apps Script कोड सैंपल में, OAuth की ज़रूरत वाले Google से बाहर के किसी एपीआई का इस्तेमाल करने के लिए, ऐड-ऑन को कॉन्फ़िगर करने का तरीका बताया गया है. इस उदाहरण में, एपीआई को ऐक्सेस करने के लिए सेवा बनाने के लिए, Apps Script लाइब्रेरी के लिए OAuth2 का इस्तेमाल किया गया है.

Apps Script

/**
* Attempts to access a non-Google API using a constructed service
* object.
*
* If your add-on needs access to non-Google APIs that require OAuth,
* you need to implement this method. You can use the OAuth1 and
* OAuth2 Apps Script libraries to help implement it.
*
* @param {String} url         The URL to access.
* @param {String} method_opt  The HTTP method. Defaults to GET.
* @param {Object} headers_opt The HTTP headers. Defaults to an empty
*                             object. The Authorization field is added
*                             to the headers in this method.
* @return {HttpResponse} the result from the UrlFetchApp.fetch() call.
*/
function accessProtectedResource(url, method_opt, headers_opt) {
  var service = getOAuthService();
  var maybeAuthorized = service.hasAccess();
  if (maybeAuthorized) {
    // A token is present, but it may be expired or invalid. Make a
    // request and check the response code to be sure.

    // Make the UrlFetch request and return the result.
    var accessToken = service.getAccessToken();
    var method = method_opt || 'get';
    var headers = headers_opt || {};
    headers['Authorization'] =
        Utilities.formatString('Bearer %s', accessToken);
    var resp = UrlFetchApp.fetch(url, {
      'headers': headers,
      'method' : method,
      'muteHttpExceptions': true, // Prevents thrown HTTP exceptions.
    });

    var code = resp.getResponseCode();
    if (code >= 200 && code < 300) {
      return resp.getContentText("utf-8"); // Success
    } else if (code == 401 || code == 403) {
      // Not fully authorized for this action.
      maybeAuthorized = false;
    } else {
      // Handle other response codes by logging them and throwing an
      // exception.
      console.error("Backend server error (%s): %s", code.toString(),
                    resp.getContentText("utf-8"));
      throw ("Backend server error: " + code);
    }
  }

  if (!maybeAuthorized) {
    // Invoke the authorization flow using the default authorization
    // prompt card.
    CardService.newAuthorizationException()
        .setAuthorizationUrl(service.getAuthorizationUrl())
        .setResourceDisplayName("Display name to show to the user")
        .throwException();
  }
}

/**
* Create a new OAuth service to facilitate accessing an API.
* This example assumes there is a single service that the add-on needs to
* access. Its name is used when persisting the authorized token, so ensure
* it is unique within the scope of the property store. You must set the
* client secret and client ID, which are obtained when registering your
* add-on with the API.
*
* See the Apps Script OAuth2 Library documentation for more
* information:
*   https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service
*
*  @return A configured OAuth2 service object.
*/
function getOAuthService() {
  return OAuth2.createService('SERVICE_NAME')
      .setAuthorizationBaseUrl('SERVICE_AUTH_URL')
      .setTokenUrl('SERVICE_AUTH_TOKEN_URL')
      .setClientId('CLIENT_ID')
      .setClientSecret('CLIENT_SECRET')
      .setScope('SERVICE_SCOPE_REQUESTS')
      .setCallbackFunction('authCallback')
      .setCache(CacheService.getUserCache())
      .setPropertyStore(PropertiesService.getUserProperties());
}

/**
* Boilerplate code to determine if a request is authorized and returns
* a corresponding HTML message. When the user completes the OAuth2 flow
* on the service provider's website, this function is invoked from the
* service. In order for authorization to succeed you must make sure that
* the service knows how to call this function by setting the correct
* redirect URL.
*
* The redirect URL to enter is:
* https://script.google.com/macros/d/<Apps Script ID>/usercallback
*
* See the Apps Script OAuth2 Library documentation for more
* information:
*   https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service
*
*  @param {Object} callbackRequest The request data received from the
*                  callback function. Pass it to the service's
*                  handleCallback() method to complete the
*                  authorization process.
*  @return {HtmlOutput} a success or denied HTML message to display to
*          the user.
*/
function authCallback(callbackRequest) {
  var authorized = getOAuthService().handleCallback(callbackRequest);
  if (authorized) {
    return HtmlService.createHtmlOutput(
      'Success!');
  } else {
    return HtmlService.createHtmlOutput('Denied');
  }
}

/**
* Unauthorizes the non-Google service. This is useful for OAuth
* development/testing.  Run this method (Run > resetOAuth in the script
* editor) to reset OAuth to re-prompt the user for OAuth.
*/
function resetOAuth() {
  getOAuthService().reset();
}