Nem is olyan régen egyszerű módot kerestem arra, hogy egy Apps Scriptet összekapcsoljak néhány API-val a Google Cloud Platformon. Találkoztam Amit Agarwal A Google Cloud API-k használata Apps Scripttel – Alkalmazásminta cikkével, és amikor megpróbáltam megvalósítani, azt tapasztaltam, hogy frissítésre van szükség, mivel mindkét platformon jelentős változások történtek. mivel eredetileg szerzője volt.

Miért tegye ezt?

Az alkalmazás, amelyen dolgozom (a cikk hamarosan…) a Google Táblázatokat használja adatforrásként egy JSON-objektum létrehozásához, amelyet a Google Cloud Storage Bucketben tárolunk. Ez az objektum nyilvános lesz az interneten, és egy statikus kezelőfelület fogyasztja. Alapvetően egy gyors és olcsó módot szeretnék egy fájl kiszolgálására valahol a Google ökoszisztémájában, és a támogatott „Advanced Google-szolgáltatások” jelenlegi listája nem tartalmazza a Cloud Storage API támogatását. Az volt a legértelmesebb, hogy az App Script közvetlenül kommunikáljon a Google Cloud tárolóval. Szóval, tokenek.

Kinek szól ez a cikk?

Ha Ön kezdő vagy középhaladó JavaScript-programozó, aki kíváncsi az OAuth-hozzáférésre, akkor ez az Ön számára készült.

Ahhoz, hogy megértsük, hogyan működik mindez, jó, ha ismerjük a következőket: gyorsítótár/gyorsítótár, POST metódus, URL-lekérés és válaszok kezelése, „Google Cloud Console”, engedélyek (általában) és a „legkisebb kiváltság elve”.

Ezenkívül tudd, hogy az Apps Script egy csomó „segédszolgáltatást” tartalmaz (UrlFetchApp, HtmlService, CacheService, PropertiesService, ScriptApp), amelyek lényegében olyan finomságok, amelyek az Apps Scripttel együtt kerülnek ki a dobozból). és kis erőfeszítéssel vagy semmi erőfeszítéssel fantasztikusak.

Ebben a gyakorlatban a következő 3 webböngészőlap között váltogathat:

  • Apps Script lap – Az Apps Script kódszerkesztő és projektbeállítások
  • GCP lap – A Google Cloud Platform konzol
  • Webes alkalmazás lap – Az Apps Script webalkalmazás

Hozza létre az Apps Script fájlokat

Hozzon létre egy Google-táblázatot, és nyissa meg az Apps Scriptet. Hozzon létre egy oauth_utilities nevű szkriptfájlt, és illessze be a következőket. Szánjon egy kis időt a megjegyzések átolvasására, hogy megismerje magát, és fontolja meg, hogyan fogja ezeket a változókat használni.

// Defines the level of access the OAuth token has to Google services and
// resources. Scope list and Documentation:
// https://developers.google.com/identity/protocols/oauth2/scopes
const scope = [
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/devstorage.read_write'
].join(' ');

// Retrieves web app URL of the current Google Apps Script project.
const webAppUrl = ScriptApp.getService().getUrl();

// Check that webAppUrl ends in /exec. Then generates callback URL for use
// as a redirect after authorization of script's access to Google account.
function generateCallbackUrl(webAppUrl) {
  if (webAppUrl.endsWith("/exec")) {
  return (webAppUrl.slice(0, -4)) + 'usercallback'
  } else {
    throw new Error('Problem with generating callback url. Web app URL issue.');
  }
}
const callbackUrl = generateCallbackUrl(webAppUrl)

// Gets a property store that all users can access, but only within this script.
const propertyStore = PropertiesService.getScriptProperties();

// Gets a copy of all key-value pairs in the current Properties store.
const scriptProps = propertyStore.getProperties();

// Gets the cache instance scoped to the script. Allows you to insert,
// retrieve, and remove items from a cache.
const cache = CacheService.getScriptCache();

Figyelem: Ismert problémák vannak a ScriptApp.getService().getUrl() inkonzisztens kimenetekkel kapcsolatban. Soha ne használja a végrehajtási naplót a webAppUrl. generálására. Ez a kimenet 2023 júniusában hibás. Itt követheti nyomon és szavazhat a problémáról: https://issuetracker.google.com/issues/285797738

A változók alá illessze be a következő buildOAuthAuthPage() függvényt, és tartson egy kis időt az átolvasáshoz és megismeréshez.

// Function to build HTML webpage for user to initiate OAuth flow.
// Checks that refresh token not present then proceeds to make state token
// and authorization url for use in HTML output. State token is used to
// maintain the state and security of OAuth flow. Authorization url takes
// required parameters documented here:
// https://developers.google.com/identity/protocols/oauth2/web-server#creatingclient
// If refresh token present, generates HTML stating authorization completed.

function buildOAuthAuthPage() {
  if (!scriptProps.refresh_token) {
    const stateToken = ScriptApp.newStateToken()
      .withMethod('googleCallback')
      .withArgument('name', 'value')
      .withTimeout(360)
      .createToken();

    const params = {
      'state': stateToken,
      'scope': scope,
      'client_id': scriptProps.client_id,
      'redirect_uri': callbackUrl,
      'response_type': 'code',
      'access_type': 'offline',
      'approval_prompt': 'force',
    };

    // Construct query string for authorization URL
    const queryString = Object.keys(params)
      .map(function (key) {
        return key + '=' + encodeURIComponent(params[key]);
      })
      .join('&');

    const authUrl = 'https://accounts.google.com/o/oauth2/auth?' + queryString;

    return HtmlService.createHtmlOutput('<div style="max-width: 1000px;       \
    margin: 0 auto; padding-top: 75px;"><h2>Step 1: Callback URL:</h2>        \
    <p>Add this callback to the OAuth Credential as an Authorized redirect    \
    URI:</p><div style="background-color: lightblue;padding: 15px;            \
    margin-bottom: 100px;"><code>' + callbackUrl + '</code></div><h2>         \
    Step 2: Authorization URL:</h2><p>After waiting ~5 minutes, visit         \
    this link to authorize the application:</p><div style="background-color:  \
    lightblue;padding: 15px;"><code>' + authUrl + '</code></div></div>');
  } else {
    // Return HTML output for already authorized case.
    return HtmlService.createHtmlOutput('Already authorized. Yippee!');
  }
}

A buildOAuthAuthPage() függvényben van egy googleCallback nevű visszahívási metódus, amelyet még nem definiáltunk, ami fontos lépés az OAuth folyamatban. Miután a felhasználó engedélyezte az alkalmazás hozzáférését a Google-fiókjához, a Google átad egy kódot az alkalmazásnak, amelyet hozzáférési és frissítési tokenre cserél.

Tehát a buildOAuthAuthPage() függvény alatt adja hozzá a következő visszahívási függvényt:

// This function is called as a callback when the user completes the OAuth
// authorization process and is redirected back to the script.
// Receives authorization code as a parameter from Google. Performs a
// fetch with POST method and required parameters documented here:
// https://developers.google.com/identity/protocols/oauth2/web-server#httprest_3
// If no error in the response, function puts access token in cache and refresh
// token in property store. Then returns HTML success page.

function googleCallback(params) {
  const url = 'https://oauth2.googleapis.com/token';

  const config = {
    "method": "post",
    "payload": {
      'code': params.parameter.code,
      'redirect_uri': callbackUrl,
      'client_id': scriptProps.client_id,
      'client_secret': scriptProps.client_secret,
      'grant_type': 'authorization_code',
    }
  };

  try {
    const response = JSON.parse(UrlFetchApp.fetch(url, config));
    cache.put("access_token", response.access_token);
    propertyStore.setProperty('refresh_token', response.refresh_token);
    return HtmlService.createHtmlOutput('<h1>Success!</h1><p>You did it! There is \
    now a refresh token in the property store and an access token in the cache.</p>');
  } catch (response) {
      throw new Error(response);
  }
}

Most, technikailag, az összes kódot implementáltuk, hogy könnyen sikerüljön… egyszer. Azt szeretnénk azonban, hogy a fő alkalmazásunk a jövőben könnyen szerezhessen vagy generálhasson tokeneket, miután egy hozzáférési jogkivonat lejárt. Ehhez egy frissítési tokent fogunk használni (a Property Store-ban mentve) új hozzáférési token létrehozásához. Jó lenne egy egyszerű módja annak, hogy ellenőrizze a hozzáférési jogkivonatot, és törölje a hozzáférési jogkivonatot a gyorsítótárból.

Ennek eléréséhez további négy funkciót kell hozzáadni a végére:

// Checks if the token is present, it returns the access token.
// If the token is not found, it calls the refreshAccessToken() function to
// obtain a new access token then returns access token.

function getAccessToken() {
  let accessToken = cache.get('access_token');
  if (!accessToken) {
    accessToken = refreshAccessToken();
  } else {
    return accessToken;
  }
}

// Makes a request to the Google OAuth token endpoint to refresh the
// access token using the refresh token. Performs a fetch with POST
// method and required parameters documented here:
// https://developers.google.com/identity/protocols/oauth2/web-server#offline
// Does not prompt user for permission.

function refreshAccessToken() {
  const url = 'https://oauth2.googleapis.com/token';
  const config = {
    "method": "post",
    "payload": {
      'client_id': scriptProps.client_id,
      'client_secret': scriptProps.client_secret,
      'refresh_token': scriptProps.refresh_token,
      'grant_type': 'refresh_token',
    }
  };

  try {
    const response = JSON.parse(UrlFetchApp.fetch(url, config));
    cache.put("access_token", response.access_token, response.expires_in);
    return response.access_token;
  } catch (response) {
      throw new Error(response);
  }
}

// Checks for access token in cache by logging to execution log.
function logAccessToken() {
  Logger.log(cache.get('access_token'))
}

// Deletes access token in cache.
function deleteAccessToken() {
  cache.remove('access_token')
  Logger.log('Manually removed access_token from cache.')
}

Az „Apps Script egy olyan funkcióval rendelkezik” doGet(), amely kezeli a webalkalmazás URL-címére küldött HTTP GET kérést. Ha URL-paramétereket használunk a doGet() függvénnyel, végig tudjuk vinni az Apps Script alkalmazás Google-fiókunkhoz való hozzáférésének engedélyezéséhez szükséges folyamatot, miközben a webalkalmazás URL-címe nagyrészt elérhető marad az alkalmazás fő funkciói, pl. egy blog vagy front-end.

Ehhez nyissa meg a Code.gs szkriptfájlt, és cseréljen le mindent a következőre, és szánjon egy kis időt, hogy megnézze, hogyan használjuk a paramétereket a forgalom irányítására.

// Function that represents the main web accessible web app. e.g. a blog.

function app(params) {
  return ContentService.createTextOutput("This could be a blog.");
}

// This function is triggered when a GET request is made to the web app URL.
// Checks if the 'oauth' parameter is present in the request parameters.
// If 'oauth' parameter is not found, it calls the app() function.
// If 'oauth' parameter is present, it calls the buildOAuthAuthPage()
// which returns HTML page for user to go through OAuth flow.

function doGet(params) {
  const initOauth = (JSON.stringify(params.parameters.oauth)) || false;

  if (!initOauth) {
    return app(params);
  } 
  else {
    return buildOAuthAuthPage(params);
  }
}

Mostanra arra a következtetésre jutottunk, hogy ennek a gyakorlatnak a kódrészét át fogjuk váltani a Google Cloud Console-ra. Néhány dolgot azonban még el kell végeznünk az Apps Script grafikus felhasználói felületén.

Tehát tartsa nyitva ezt az Apps Script lapot.

Hozzon létre egy Google Cloud Platform projektet

Egy új lapon nyissa meg a „Google Cloud Platform” alkalmazást. Hozzon létre egy új projektet, és ellenőrizze, hogy ki van-e jelölve. Nyissa meg az „OAuth hozzájárulási képernyőt” az „API és szolgáltatások” irányítópulton.

A típushoz válassza a Külső lehetőséget. Adja meg az alkalmazás nevét, a felhasználói támogatási e-mail-címet és a fejlesztő kapcsolatfelvételi adatait. A körök üresen hagyhatók. Hozzon létre egy tesztfelhasználót a jelenleg használt Google-fiókkal, majd kattintson a Mentés és folytatás gombra.

Ezután lépjen a Google Cloud Project Welcome/Home oldalára, és másolja ki a projekt számát.

Ezután visszatérünk az Apps Scripthez, de ezt a GCP lapot nyitva tartjuk.

Csatlakoztassa a Google Cloud Projectet az Apps Scripthez

Az Apps Script lapon nyissa meg a Projektbeállításokat, lépjen a Google Cloud Platform Project oldalra, majd kattintson a Módosítás gombra. Illessze be a fentről másolt projektszámot.

Hozza létre az OAuth-hitelesítési adatokat, és mentse az azonosítót és a titkos adatot szkripttulajdonságokként az Apps Scriptben

A GCP lapon nyissa meg a "Hitelesítési adatok" panelt az "API és szolgáltatások" irányítópulton. Kattintson a +Hitelesítési adatok létrehozása lehetőségre, és válassza az OAuth-ügyfélazonosító lehetőséget.

Az alkalmazás típusához válassza a Webes alkalmazás lehetőséget, és adja meg a nevet.

Az Engedélyezett JavaScript eredetpontok részhez adja hozzá a következő URI-t:
https://script.google.com
Erre azért van szükség, mert a JavaScriptet használó alkalmazásoknak biztonsági okokból azonosítani kell a tartomány eredetét. További információ: Google dokumentáció az engedélyezett JavaScript eredetekről.

Az engedélyezett átirányítási URI-k mezőt egyelőre hagyja üresen. Ide fog menni a visszahívási URL-ünk, de még nem tudjuk az értéket. A visszahívási URL-t akkor használja a rendszer, ha a felhasználó befejezte az engedélyezési folyamatot, és a Google készen áll a sikeres válasz (access_token és refresh_token) visszaküldésére az alkalmazásnak. Mivel vannak tokenek küldése, a Google szeretné előre tudni, hogy pontosan hova kell azokat küldeni, és hogy minden tökéletesen egyezik-e a kérésünkben a authUrl queryString-jében a 'redirect_uri': callbackUrl, paraméteren keresztül megadottal. Alapvetően képzeljünk el egy kulcsot, amely elkészítésekor csak egy helyre küldhető. Ez annak ellenőrzése, hogy a hely következetes-e.

Hamarosan visszatérünk az engedélyezett átirányítási URI-khoz.

Kattintson a Létrehozás gombra.

Ezután lépjen újra az Apps Script lap projektbeállításaihoz. A Szkript tulajdonságai alatt kattintson a Szkripttulajdonság hozzáadása lehetőségre. Hozzon létre két tulajdonságot pontosan az alábbiak szerint: client_id client_secret

Ezután másolja ki az ügyfél-azonosítót és az ügyféltitkot a hitelesítő adatokról a GCP lapon, és illessze be ide a megfelelő tulajdonságba.

Mentse el ezeket a tulajdonságokat.

Megjegyzés: amikor létrehoztuk a két változót (lásd alább) a kódolási rész elején, ugyanahhoz az alkalmazásszkript-erőforráshoz fértünk hozzá, mint most a grafikus felhasználói felület használatakor. Ezeket a szkripttulajdonságokat programozottan is be lehet állítani, de szeretem a titkokat a lehető legtávolabb tartani a kódtól, és nem szeretem a kódban tárolt érzékeny információkat még ideiglenesen sem. Ezért kerülöm ezt:
propertyStore.setProperty('client_id', 'someSuperSecretValue');

const propertyStore = PropertiesService.getScriptProperties();
const scriptProps = propertyStore.getProperties();

// ... //
  'client_id': scriptProps.client_id,
  'client_secret': scriptProps.client_secret,
// ... //

Telepítse az Apps Scriptet, és írjon alá egy csomó szerződést (más néven engedélyezze az alkalmazást a Google-fiók használatára)

Most, hogy létrehozta az Apps Scriptet, a Google Cloud projektet, az OAuth hitelesítési adatokat, és összekapcsolta őket, készen állunk az Apps Script webalkalmazásának üzembe helyezésére és a hitelesítő adatok egy apró frissítésére az utolsó lépés előtt.

Ebben a lépésben a Google felkéri Önt, hogy engedélyezze a hozzáférést, vagy erősítse meg, hogy melyik Google-fiókkal kíván folytatni. Ügyeljen arra, hogy azzal a Google-fiókkal lépjen tovább, amelyet tesztfelhasználóként adott meg a korábban létrehozott hitelesítési adatokhoz.

Az Apps Script lapon kattintson a jobb felső sarokban található Telepítés gombra, és válassza a Telepítések kezelése lehetőséget. Ezután válassza az Új telepítés lehetőséget. A telepítés típusához válassza a Webes alkalmazás lehetőséget. A gyakorlat céljaira az alkalmazást „Én” néven és „Csak én” hozzáféréssel futtathatja. Kattintson a Telepítés gombra.

Megjelenik egy párbeszédpanel a webalkalmazás URL-címével.

Másolja ki ezt az URL-t, és fűzze hozzá a oauth paramétert az URL legvégéhez (a /exec után) így. Jegyezze meg a ? jelet, amely a paraméterek kezdetét jelöli.

Például:
https://script.google.com/macros/s/AKMwefJO330202MsmwX3_fomiWAICWM929gjKE31b5FE/exec?oauth

Ez a oauth paraméter megmondja a doGet() függvénynek, hogy végre kívánja hajtani a initiatiateOAuth() függvényt. Amint azt korábban említettük, a doGet() funkciót is elérhetőbben tartja bármihez is, amit végül megépít. Például használhatja a doGet() függvényt egy blog vagy weboldal kezelőfelületének biztosítására.

Nyisson meg egy új lapot, és keresse fel ezt az URL-t a oauth paraméterrel. Ez az oldal a korábban létrehozott buildOAuthAuthPage() függvény return HtmlService.createHtmlOutput(...) része.

Másolja ki a /callback-ra végződő visszahívási URL-t

Tartsa nyitva ezt a Webes alkalmazás lapot.

Menjen vissza a GCP lapra, és a Hitelesítési adatok panelen szerkessze a korábban létrehozott hitelesítő adatokat. Lépjen le az Engedélyezett átirányítási URI-k szakaszhoz, amelyet korábban üresen hagytunk, és kattintson az URI HOZZÁADÁSA lehetőségre.

Illessze be az imént kimásolt visszahívási URL-t, és győződjön meg arról, hogy az azonos, szóközök nélkül, stb. Most a hitelesítő adat tudja, hova küldheti el a felhasználót az engedélyezési folyamat után.

Mentse el a változtatásokat, és jegyezze fel az időt. A Google szerint ez körülbelül 5 percet vagy órát vehet igénybe, amíg a változás bekövetkezik. Tapasztalataim szerint ez elég azonnali volt.

Ezután lépjen vissza a 2. lépéshez a Webes alkalmazás lapon, és másolja ki a második URL-t. Egy új lapon navigáljon erre az URL-re, és írjon alá egy csomó további szerződést.

Ha minden működött, akkor „Siker”-t kapsz! üzenet.

Ellenőrizze a frissítési és hozzáférési tokeneket az Apps Scriptben

Az Apps Script lapon lépjen a szerkesztőpanelre (ahol az összes JavaScript található), és válassza ki a getAccessToken() függvényt, és futtassa.

Ezután válassza ki a logCache() függvényt, és futtassa.

A végrehajtási napló megjeleníti a hozzáférési tokent, ha sikeres.

A frissítési token törlése (manuálisan)

Ha törölni szeretné a frissítési tokent az alkalmazás újbóli engedélyezése érdekében, vagy valamilyen más okból (talán megváltoztatta a hatókört), visszatérhet az Apps Script projektbeállításaihoz, és törölheti a refresh_token nevű szkripttulajdonságot.

Építs valami klasszat…

Így most már rendelkezik egy hozzáférési tokennel, amelyet a Google Cloud Platformon és a Cloud Storage API-n használhat. Ha azt szeretné, hogy a token más API-kkal is működjön, módosítsa az elsőként meghatározott hatókörváltozót.

// Scope list and Documentation:
// https://developers.google.com/identity/protocols/oauth2/scopes
const scope = [
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/devstorage.read_write'
].join(' ');

Íme egy jó kiindulópont a Google API-k hívásához:

https://developers.google.com/identity/protocols/oauth2/web-server#callinganapi

Légy óvatos…

Ez a cikk valójában csak gyakorlat, és nem feltételezhető, hogy egy termelési szintű biztonságos alkalmazás. Legyen tehát elővigyázatos, és őrizze meg saját és mások adatait.

Fontolja meg, hogyan valósíthatja meg a „legkisebb kiváltság elvét” ebben a rendszerben. Például az OAuth-hitelesítési adatokat tovább lehet zárni valahol hatókörrel?

Elolvasás után égesd el…

Az alábbi két lépést követve mindent megsemmisíthet, amit létrehozott:

1.) Látogassa meg a GCP Project Settings oldalt. Győződjön meg arról, hogy a megfelelő projektet választotta! Kattintson a Leállítás gombra.

2.) Látogassa meg az Apps Script Projects oldalt. A jobb szélen található hárompontos menü segítségével válassza ki a megfelelő projektet, majd válassza a Végleges törlés lehetőséget.

Külön köszönet Colin King-Bailey-nek a szakértői értékelésért és a javaslatokért.

Segítségre van szüksége Apps Script vagy Web Development munkájában?

Érje el webfejlesztési projektjét még ma. Íme egy rövid lista azokról a technológiákról, amelyekkel dolgoztam:

  • WordPress
  • Django
  • Drupal
  • Reagál
  • Google Apps Script

https://www.linkedin.com/in/jessewoltjer/

Google Apps Script, Google Cloud Platform és JavaScript logók