Google Data Studio Community Connector: Accessing URL Parameters in App Script - google-apps-script

I am following the Row level filtering with Embedded dashboards tutorial to build a secure, multi-tenant dashboard using Data Studio as the reporting UI/backend (and Anvil as the front-end in Python), with the aim of filtering data from a BigQuery table based on specific user permisisons.
I have completed the initial connector tutorial successfully as recommended. So far I have also completed the following steps (1-4) successfully :
Build user authentication in Anvil and set permissions in user table (JSON containing specific field values each user is permitted to view)
Build mechanism to create access_token in Anvil, based on hex md5 hash of a combination of user and unix timestamp, with one hour expiry
Set and store app_secret securely in Anvil to authenticate inbound requests
Create API endpoint in Anvil which validates the app_secret and validates access_token and returns JSON containing user_email and permitted_accounts
Now I've got to the point of building the custom connector, with the aim of building the next part of the flow:
Pass access_token to Data Studio by encoding and including it in the embed URL (the report will be embedded via an iFrame) as per this section
Extract access_token from embed URL in App Script as per the statement "This will be used to capture a token from embed URL's parameters." from this section
Hit the API endpoint with the app_secret and access_token from App Script to validate and receive the user permissions associated with the access_token IF validation checks pass in Anvil, otherwise return appropriate error message
Construct query based on user permissions and get data from BigQuery using this approach
... then set schema etc...
However, upon reaching step 6 in this flow where I need to parse the access_token from the URL, the code included in the Write the Connector Code section takes the token as a user-inputted text field and does not take it from the embed URL's parameters (which to me makes no sense as making a user continually manually transpose a short-lived token seems to negate the point of this exercise).
I am also unable to find documentation in the API reference on how to achieve this.
PLEASE, does anybody know how to capture parameters from Report Embed URLs in an App Script Community Connector? I assumed it was possible from the documentation:
getConfig() should return at least one config item. This will be used to capture a token from embed URL's parameters.
Thank you for taking the time!

a. I'm assuming access_token is an overridable config parameter in your getConfig.
b. When you create the initial report, during the data source creation, you can put in any value in access_token config field. However, check the box to 'Allow "access_token" to be modified in reports'. That means, report viewers will be able to override this value even though they do not have edit access to the report or the data source.
c. Confirm that your setp #5 is following the instructions here to pass the override value for access_token.
d. That's it. Now it is irrelevant what value you put in for access_token during initial report creation. For user X, your portal was pass the value hash(user, timestamp) as the value for access_token. In your connector's getData function, request.configParams.access_token will return that specific value. You can then call your endpoint with that value to get back the user identity.

Related

Using BigQuery as a datastorage/cache system for google sheets addon

I have a google sheets addon with custom formulas that fetch data from my API to show in their result.
The problem for many users is the addon frequently reaches the Urlfetch quota. So I'm trying to use another source of data for my formulas, I been trying to setup BigQuery for that ( I know is not meant to be used like that).
My approach would be something like this:
When a user executes a formula, I look first in BigQuery to see if data already there, if not fetch from API then stores the result in a BigQuery.
I tried a proof of concept, where I added a custom function to my addon with the code sample in https://developers.google.com/apps-script/advanced/bigquery
where I replaced projectId for my own and queried a sample table
when executed the formula got this error:
GoogleJsonResponseException: API call to bigquery.jobs.query failed with error: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
I tried also execute the same code from a function called from the sidebar frontend, where I got this error instead:
User does not have bigquery.jobs.create permission in project
From these errors I'm guessing I have to assign a BigQuery role in IAM to each user. But I have hundreds of them.
Is there any why that any addon user can access the BigQuery project? or my whole approach is wrong.
How about using the Cache service instead of BigQuery? The Cache service is simple to use, does not require authentication, and also works in a custom function context.
Note these limitations:
The maximum length of a key is 250 characters.
The maximum amount of data that can be stored per key is 100KB.
The maximum expiration time is 21,600 seconds (6 hours), but cached data may be removed before this time if a lot of data is cached.
The cap for cached items is 1,000. If more than 1,000 items are written, the cache stores the 900 items farthest from expiration.

Google Data Studio Community Connector: Passing Token Through Embed URL Not Working

I'm struggling to get a community connector to work for a web-app I'm building.
I wrote a very basic connector in App Script to do row level filtering on an embedded dashboard, using these docs Google provides: https://developers.google.com/datastudio/solution/viewers-cred-with-3p-credentials
My connector uses getConfig() and getData() to pass a token into an API endpoint, and that API endpoint returns data specific to that user.
The connector works perfectly when I pass in a token in the GUI by modifying the configuration, but doesn't work when I try to pass in the token using the embedded url.
Example 1
User Token = 5wKu4QLvc9PC7tkPyg
Steps:
Click "Edit connection"
Enter token in input field [which is generated by getConfig() in app script]
Click "Reconnect"
Click "Apply" on dialogue box that comes up
Google Data Studio correctly retrieves 3 rows of data for this token
Example 2
User Token = z6ps6Vhb9hzB333gG4
Steps:
Click "Edit connection"
Enter token in input field [which is generated by getConfig() in app script]
Click "Reconnect"
Click "Apply" on dialogue box that comes up
Google Data Studio correctly retrieves 1 row of data for this token
The primary issue
When I use the embed URL for the dashboard, I noticed the App Script execution logs aren't showing that getConfig or getData operations are being run.
The end result is the token doesn't appear to be passed into the embed URL at all. The data remains the same as in the GUI-based report.
However I can tell the embed URL is being invoked, because it does call getAuthType in the App Script execution logs, see image below. (I made the change in the GUI at 9:50:59 PM, and then the subsequent timestamps are me attempting to hit the embed URL)
Sample
Here's the sample of the actual embed URL I'm using:
https://datastudio.google.com/embed/reporting/9dea8b8a-5b51-4add-adb2-afda3861b241/page/VXkvC?config=%22%7B%5C%22token%5C%22:%20%5C%22z6ps6Vhb9hzB333gG4%5C%E2%80%9D%7D%E2%80%9D
Per the Example 2 above, the token being used in this embed URL should only return 1 row of data, but it's returning all 3.
I followed the Google docs, which suggest this is the format to use: https://datastudio.google.com/embed/reporting/[report_id_here]/page/[page_id_here]?config="{\"token\": \"[token_here]\”}”
I'm wondering if it's possible the Google docs are incorrect (or) out of date and my embed url is malformed? I can't get my head around this issue.
Resolution
Despite being part of the getConfig(), apparently you also have to "Add a Parameter" for the token to your Data Source on the report. This will result in the parameter being visible under all fields.
Next, go to Resource > "Manage report url parameters" and check the box for "Allow to be modified in report URL". Take note of the "Name" field. Mine was ds0.token
Finally, use this URL syntax: https://datastudio.google.com/embed/reporting/[report_id_here]/page/[page_id_here]?params=%7B"[name_from_step_2]":"[value]"%7D
Which for me translated into:
https://datastudio.google.com/embed/reporting/9dea8b8a-5b51-4add-adb2-afda3861b241/page/VXkvC?params=%7B"ds0.token":"12345"%7D
Which in raw URL format looks like:
https://datastudio.google.com/embed/reporting/9dea8b8a-5b51-4add-adb2-afda3861b241/page/VXkvC?params=%7B"ds0.token":"12345"%7D
One additional observation. I was watching the logs on my API endpoint, and I noticed Google caches any data it brings back from the endpoint. So don't be surprised if you don't see a getData() call in your App Script execution logs for any tokens you've already passed. Just make up a new token to see a new getData execution log.

Get User's first and last name via Google API

Currently I am developing a Chrome-GMAIL extension which requires me to get the logged in user's first and last names. For experimentation, I have used the following goggle API (userinfo) and have successfully obtained the names I wanted:
https://www.googleapis.com/auth/userinfo.profile
However, using the userinfo APIs will cause a change in the OAuth2 scopes in my manifest. This change will in turn cause a permission-prompt to my existing users (if a domain wide delegation is not setup in place). Point being the idea of having more prompts in front of my user, or additional oauth scope is not really something I desire.
Currently our extensions use the following OAuth scopes and API :
Chrome's Identity API
Chrome's Storage API
GMAIL.modify
GMAIL.send
My question is, is it possible to get the first and last names using an API that is defined/allowed/provided for by any of the above scopes/permissions I listed? or is userinfo the only way to go?
Thank you very much.
Profile data like first name and last name is private data. You are corect that some Google apis give you access to some data that would normally require an extra scope. For email normally you would need to to request the email scope to get this back however the Gmail api does have an endpoint getprofile which will return the current users email address without you requesting the email scope.
However i am not aware of any apis that will give you access to the users first and last name without you requesting the profile or user.profile scope.
If you do decide to add the scope, I do recommend going though the people api rather then the userinfo endpoint as the data returned by the user info endpoint is not guaranteed to always return the name.

Which authentication can be used for managing Box users through REST end points

I am working on an Identity management application, using which my goal is to manage users on Box application.
I was going through Box documentation, and there are two ways for authentication
OAuth 2.0, which has redirection URI as required parameter. And due to which I cannot make use of it, since I will not be able to enter username and password and Authorize dynamically using my Java code.
Reference: https://box-content.readme.io/reference#oauth-2
JWT authentication, this I can use in my code and successfully get Access token. But problem here is, this access token can only be used to manage App Users (who will not have login to Box website).
Reference: https://box-content.readme.io/docs/box-developer-edition
So, is there any other authentication mechanism which I can use for getting Access token for managing Box users?
Regards,
Sandeep
The current best option is #1 with a process like this:
Create a Box application with the 'Manage an Enterprise' scope enabled.
Use a web-based access token generator (such as this or this) to get an initial access/refresh token pair. Save these somewhere safe (flat file, DB).
Code your application to initialize itself with the access/refresh token pair from its saved location.
When the access/refresh token pair is refreshed, write them out to the save location.
If your application runs across multiple nodes/processes this approach will require some (painful) coordination between them. I believe Box is working on some improvements in this area, so you may not have to live with this for long.

App scoped user ids

I have two facebook applications with their own access tokens.
I have followed this guide to create a business and associate the aforementioned apps to this business.
I now have a token for business from doing a graph api call -
/me?fields=token_for_business
When doing a regular api call without referencing this token for business, I can return app scoped user ids who have been invited to events for example like so -
https://graph.facebook.com/v2.3/{FaceBookGroupID}/invited?access_token={MyAppsAccessToken}
Where MyAppsAccessToken varies depending on the app, thus resulting in different app scoped user ids being returned depending on what access token is used.
After getting the token_for_business I updated my call to -
https://graph.facebook.com/v2.3/{FaceBookGroupID}/invited?token_for_business={MyTokenForBusiness}&access_token={MyAppsAccessToken}
My assumption was that now the applications are associated to the same business and have a link token, the calls would have brought back the same user ids in the JSON response, however this is not the case and regardless of appending the token_for_business parameter to the url, the same JSON responses are returned as the first call.
Is it possible to return the same user ids with two different access tokens?
No, different Access Token means different App, means different App Scoped ID. You can only use the Token for Business to match different App Scopes.
Btw, the call with token_for_business as parameter does not make any sense. The token is for one user.