OAuth wordt gebruikt om de toegangsrechten te regelen tot een externe lib of data.
Bij iedere aanvraag van data of interactie met een externe service, moet de OAuth de nodige credentials leveren.
De procedure is vrij ingewikkeld en men heeft hiervoor een API ontwikkeld, die vrij ter beschikking staat.
https://github.com/googleworkspace/apps-script-oauth2
In de Sample folder vind je stukken code die je kan gebruiken of inzien waar de Basecamp zowat de meest essentieel versie is.
De key om de lib toe te voegen is:
1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF
Je kiest de laatste versie.
Kopieer de code van Basecamp naar je Apps script code.gs.
Standaard wordt OAuth afgehandeld via een redirect URI, dus je zal steeds zulk een functie kunnen terug vinden in de code.
Voer in de debug mode de volgende functie uit, waardoor Apps script je een OAuth2 toestemming vraagt:
function logRedirectUri()
Zie een integratie van het geheel in de volgende video: https://youtu.be/LrFKAoyQwj4
De code is nodig om de aanvraag te starten via een REST API (niet de standaard BigQuery API) is.
Gebruik de REST API enkel als je de oplossing niet kan via de Advanced API, die je standaard in je script kunt gebruiken in je Apps Script.
Het voorbeeld hier wordt gebruikt om aan te tonen hoe je ook met externe code (node.js etc) gebruik kan maken van de Google API’s via REST.
Als je een user wil toevoegen aan de data (insertUserIntoBigQuery) dan moet je dus een aantal stappen doorlopen, alvorens dit mogelijk zal zijn.
De Query string bevat de commando’s, Query, die uitgevoerd gaan worden door de server en dus de data die in de datatable terecht zullen komen.
Het is een SQL commando met de nodige parameters.
De functie runQuery zal een aantal taken moeten doen.
Bepaal een unieke naam voor de serviceName (hier BigQuery)
Bepaal de scope van je service, deze scope is voor iedere servicetype verschillend (zie Authorization scopes)
Het GCP project waaruit de JSON key komt voor autorisatie, hier als voorbeeld "service-accounts-article"
Bepaal de string die gebruikt wordt met de specifieke query aanvraag (instructies voor de request URL, zie BigQuery API Query).
Je maakt hier gebruik van een URL, die verwijst naar een job.query method.
Door useLegacySQL op false te zetten kan je moderne SQL gebruiken.
Pak alles samen in een JSON file (payload)
Doe nu een sendRequest met de juiste parameters zodat wat je wenst wordt afgehandeld.
De SendRequest moet een aantal zaken afhandelen voor het bekomen van de toegang tot de service.
Uit de JSON string OAuthParams haal je de gegeven van de service account, wie je moet impersonaten, de scope en de serviceName
Je doet een toegangsaanvraag naar de server via de createService, die via OAuth2.createService een key zal aanmaken waarnaar je script kan verwijzen .
Indien de hasAccess positief is, kan de rest verder aangevuld worden en gebruik je de token voor het genereren van een bearer header getAccessToken.
Je geeft nog wat extra opties mee in de uiteindelijke aanvraag : post met contenttype en
Als antwoord geef je het JSON resultaat op de URL vraag dmv een UrlFetchApp, wat in de response constante zal terecht komen en in principe goed verloopt.
Op deze wijze wordt de vraag naar een SQL query in BigQuery steeds afgehandeld door het aanvragen van toegangsrechten, samen met de eigenlijke service vraag.
Het antwoord zal een json object zijn waarin de job Complete kan gebruikt worden voor controle.
Verdere info kan eveneens nuttig zijn om te achterhalen of de aanvraag correct is gebeurt.
Wanneer je in Google wil gebruik maken in Apps script van een service account om toegang te krijgen tot een service, doe je dit meestal om het probleem te omzijlen om "per gebruiker credentials" te moeten toekennen in GCP en AIM, een taak die bij de domain beheerders ligt.
Het idee van een sercives account is dat er een generieke gebruiker bestaat die rechten heeft op een service of een deel van de service.
Dit account wordt dan gebruikt door allerhande clients om tot dezelfde service toegang te verkrijgen, waardoor de beheerders niet telkens nieuwe users moeten toelaten tot de service.
Echter dit vormt een "security risk" als men de credentials van het SA zou integreren in de code.
Hierdoor zou bij het maken van een kopij van een bestaand document, de credentials toegankelijk worden, wat niet de bedoeling kan zijn.
Om dit probleem op te lossen werken we met een 2 stappen toegang.
De JSON private key file van het Service account wordt op een plaats gezet waar enkel een beperkt aantal leden van je domain toegang hebben, de "configuratoren" van een tool.
Bij het activeren van een Apps script op een nieuw document worden de credentials door deze "configuratoren" ingelezen in de Document properties.
Hierdoor worden deze vernietigd indien iemand een kopij maakt van het document om er andere dingen mee te doen.
Vermits de toegang tot de JSON file beperkt is tot deze configuratoren, kan deze op een shared folder geplaatst worden en kunnen er zo meerdere personen de rol opnemen.
De code die kan gebruikt worden om deze gegevens in te lezen is de volgende: JSON File read.
Door dit aan te roepen via een add-on menu in een sheet, kan je bij het configureren van de tool deze actie éénmalig laten uitvoeren door een "configurator".
.addItem('Config Service Account connection', 'setKeyFile')
Hierna zijn de gegevens bekend binnen dit document.
/**
* Get keyfile URl and save it to a userPoperty of the script.
* Set installable triggers onFormSubmit and OnEdit
*
*/
function setKeyFile() {
try { //Set userProperty
var ui = SpreadsheetApp.getUi();
var userProperties = PropertiesService.getUserProperties();
//const Key_file = 'api.key';
var docProperties = PropertiesService.getDocumentProperties();
//config only 1 time per document
let collectionval = docProperties.getProperty('Collection')
Logger.log(`Collection =${collectionval}`)
if (collectionval == "" || collectionval == null) {
var uiValue = ui.prompt('Please provide your Private key file URL.', ui.ButtonSet.OK);
userProperties.setProperty('Key_file', uiValue.getResponseText());
Logger.log(`readfile =${uiValue.getResponseText()}`)
const read = readKeyFile(userProperties.getProperty('Key_file'));
if (read) {
Logger.log(`readfile =${read}`)
uiValue = ui.prompt('Please provide FireStore Collection for quizzes.', ui.ButtonSet.OK);
docProperties.setProperty('Collection', uiValue.getResponseText());
docProperties.setProperty('RecCount', 0)
Logger.log(`Collection= ${uiValue.getResponseText()}`)
//Create installable trigger for form submission and onEdit
var sheet = SpreadsheetApp.getActive();
// Trigger every 6 hours.
ScriptApp.newTrigger('Crawler')
.timeBased()
.atHour(6)
.everyDays(1)
.create();
} else {
ui.alert(`Unable to read key file`)
}
} else {
ui.alert(`Connection exist to Firestore DB collection /${docProperties.getProperty('Collection')}`)
}
} catch (e) {
ui.alert(e)
}
}
Als voorbeeld zie je hier de toegang tot de pub/sub service via een REST Api dmv een Service account, aangemaakt door het inlezen van de credentials in de documentProperties.
Het SA moet toegang hebben tot de pub/sub met de volgende credentials pub/sub admin of editor.
/**
* getPubSub Service with Service account
*/
function getPubSubService() {
const docProperties = PropertiesService.getDocumentProperties();
const config = {
'project_id': docProperties.getProperty('project_id'),
'private_key': docProperties.getProperty('private_key'),
'client_email': docProperties.getProperty('client_email')
}
return OAuth2.createService('SpreadsheetPubSub')
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/v2/auth')
.setTokenUrl('https://oauth2.googleapis.com/token')
.setPrivateKey(config.private_key)
.setIssuer(config.client_email)
.setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getDocumentProperties())
// .setCache(CacheService.getUserCache())
// .setLock(LockService.getUserLock())
.setScope(['https://www.googleapis.com/auth/pubsub','https://www.googleapis.com/auth/script.external_request'])
.setParam('access_type', 'offline')
.setParam('approval_prompt', 'force')
.setParam('login_hint', Session.getActiveUser().getEmail());
}
function authCallback(request) {
var service = getPubSubService();
var isAuthorized = service.handleCallback(request);
if (isAuthorized) {
closeSidebar(); // optional
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
// call this to re-test authorization
function reset() {
var service = getPubSubService();
service.reset();
}
function logRedirectUri() {
Logger.log(OAuth2.getRedirectUri());
}
Om in een persoonlijk account toegang te krijgen, dien je een interface te bouden in je app, waarbij de privacy concent wordt afgehandeld.
De code vind je terug op : OAUTH afhandeling personal account .
Houd er rekening mee dat je bij het verlenen van de credentials een Administrator nodig hebt, die jouw persoonlijk account toevoegt aan de service en die je de key bezorgt.
De code van je key kan je inlezen of in je code plaatsen (let op, dat niemand je code kan kopiëren)