How to fetch Google OAuth access tokens from Chrome extensions - tradeoffs between options - google-chrome

EDIT: In case it helps others who find this, the fact that Gaia clears cookies after Chrome restart is known behavior. They recommend the use of Chrome login. See more here.
The core question: how do I fetch Google OAuth access tokens in the background from a Chrome extension without using the getAuthToken method in the chrome.identity API? This method forces users to login to Chrome, which isn't ideal for our app. There's also no way I can find to retrieve OpenID Connect ID tokens using the native methods exposed in the identity API.
Currently, I'm fetching tokens using the launchWebAuthFlow method from the chrome.identity API. This method launches an "Identity API Scope Approval UI" window in which the user makes the initial OAuth grant. This window (referred to as "gaia") has its own session / cookie data, separate from a browser session.
After the initial OAuth grant, the extension periodically retrieves access tokens in the background, and subsequently fetches data from Google APIs (e.g. Drive). This works well. When a user quits and re-opens Chrome, however, fetching access tokens fails - gaia loses its session cookies when Chrome restarts, so running the launchWebFlowAuth method doesn't complete because Google can't authenticate the user (unless we prompt the user to login in the Identity API Scope Approval UI again). Others seem to have had the same issue.
Google Sign-In would be ideal, but it doesn't work in Chrome extensions (I'm getting the same "Invalid cookiePolicy" error as the author of that issue).
Any advice on how to best handle this use case would be very much appreciated!

Related

Google Calendar API - 403 error

I am attempting to set up an application using the Google Calendar API. I set up all the credentials and set the authorized JavaScript page equal to http://localhost:8000 as suggested within the quick start guide at https://developers.google.com/google-apps/calendar/quickstart/js. When I try to run the file on localhost:8000/quickstart.html it will show the basis page but no information. I can click the authenticate button and it will allow me to authenticate my account to use the Calendar application, however nothing will show up information-wise. Checking the console, the following errors are present:
Get https://content.googleapis.com/calendar/v3/calendars/primary/events?maxResults=10&orderBy=startTime&showDeleted=false&singleEvents=true&timeMin=2017-08-11T20%3A58%3A29.156Z 403 ()
and
Uncaught {"error":{"errors":[{"domain":"usageLimits","reason":"accessNotConfigured","message":"Access Not Configured. Calendar API has not been used in project 440480151645 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/calendar.googleapis.com/overview?project=44080151645 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.","extendedHelp":"https://console.developers.google.com/apis/api/calendar.googleapis.com/overview?project=44080151645"}],"code":403,"message":"Access Not Configured. Calendar API has not been used in project 44080151645 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/calendar.googleapis.com/overview?project=44080151645 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."}}
However, going to the supplied link only says that I do not have access to the calendar.googleapis.com api and will not let me enable my Google Calendar or use it.
Google calendar uses Oauth2.0 for authorization. There are three essential components of this. If any of these are missing, authorization will not happen properly and you'll get a 403.
Access token - the google calendar API saves an access token to your
/.credentials folder by default. Your application will need to be
able to read this file (wherever it may be), otherwise you will have
to manually authorize API calls with every request.
client_secret.json - this is a necessary file to authorize your
application itself as a client of google. When you created a clientID and such using the google developer portal, this file was
created, and must also be read by your application.
Scopes - The scope is what level of access you have. It is by
default in the quickstart set to "read-only", which could be why
you're getting 403. If you want to change the scope, you're going to
have to edit both the line of code in the quickstart and either
modify the access token or get a new one to reflect that change.
I have gotten this error before, but that was before I understood Oauth2. If the problem isn't in any of these, it's likely something that you selected in the developer console. Let me know if you are still stuck.
Based from this thread, try setting the Referrers to Any referrer allowed for your project (just leave the field empty) in the Google Developers Console if it is not already like that.
To do this, go to your Google Developers Console and open API & Auth / Credentials and click Edit allowed referrers empty the input field.
Additional reference:
Google Calendar API v3 Access Not Configured
403 error with message:Access Not Configured. Please use Google Developers Console to activate the API for your project
Access Not Configured. The API (Google+ API) is not enabled for your project. Please use the Google Developers Console to update your configuration

Is it possible to skip OAuth2 consent screen when using chrome.identity API?

I have been developing an extension, which is using the chrome.identity API (chrome.identity.getAuthToken method) in order to receive the OAuth2 token for signing up extension users via Google+.
I would like to push this extension in a company via Google Admin Console, but when chrome.identity.getAuthToken method is being called, our extension users are present with the "Identity API Scope Approval" screen - the consent screen, where users have to authorize extension's access to scopes we want access to.
I am aware, how process of getting OAuth2 access tokens works in general, and that user's approval is normally required in order to receive the OAuth2 token.
However, is it possible to automatically skip & authorize this OAuth2 consent screen only for users, whose extension was deployed via Google Admin Console?
I've found an interesting on Chromium Bugs list, where OP states:
We are working on integrating Quickoffice for Chrome (which is a chrome extension) with the Drive Web UI. In order to handle OAuth we use chrome.identity, which works great.
and also the following:
since our extension is white listed as a first party app, we by pass the user content screen for the OAuth flow (which is what we want for the seamless integration with drive web ui).
I was not able to find any information, how to neither whitelist the extension as a first party app in order to prevent the consent screen from showing up, nor how to automatically authorize extension's access to those API scopes.
Any ideas?
If you include identity.email in the manifest permissions, and it is a force-installed extension or app through the admin console, then you will be able to get the user's identity and also the auth token without user oauth prompt approval.
However this only allows you to get the user's identity, no extended permissions. Any extended permissions (such as gdrive access, etc) need an oauth prompt.

Handling tokens in Google Drive

I went through the Quickstart on how to upload a file to Google Drive (for Android) and everything works fine. However, it isn't clear whether I am responsible for storing tokens and handling exceptions if they expire. Does the SDK code used in the Quickstart handle this for me behind the scenes?:
https://developers.google.com/drive/quickstart-android
If I regularly call this code (taken from the Quickstart):
credential = GoogleAccountCredential.usingOAuth2(this, DriveScopes.DRIVE);
credential.setSelectedAccountName(accountName);
service = getDriveService(credential);
and then call some drive method, will it eventually generate an exception when the token expires or does the SDK code catch this internally and automatically attempt to retrieve a refreshed token?
To be even more specific, am I required to implement the code shown here:
https://developers.google.com/drive/credentials
What also isn't clear to me is the difference between an access token and refresh token. Then there is "short lived" tokens and "long lived" tokens. Kind of confusing.
On Android, when you use Google Play Services, all of the work is handled for you, including getting the token and refreshing it. This is explained in the quickstart guide that you have linked, and there is nothing more that you need to do.

Using OAuth2.0 (Google API) on a kiosk

I'm trying to set up a PC in kiosk mode that will display a list of events from a Google Calendar. I initially wanted to use the Google API for this (be it the JS client, Python client or other), but it seems that all of these require the user to consent via a page in the browser. This is not acceptable because it's a standalone system--there is no user to click on anything. The system will be on a daily power cycle so a 'consent once run forever' is also not possible.
Is there a way to use the authenticated (OAuth2.0) Google API to access calendar data without any user intervention whatsoever (except just a one-time initial setup)?
Thanks!
That's exactly what OAuth should be able to do. You should do a onetime setup (start he flow, authorize the request and get the tokens) and then store the tokens. Once the tokens are stored, you do NOT need any more user action as long as you have the tokens. The tokens would then be used to retrieve the data from the Google Calendar.

Google Script OAuth for multiple users

I've created a Google App Script that handle 2 different OAuth connections.
1- Google itself to send mail on behalf of the user and access google docs (google api console used to get keys, secret)
2- gtraxapp wich is a timesheet cloud-based app. (Script is registered, got a key/secret, etc.)
The script is published as a web app. It works perfectly for my user.
When logged on a different user name, I can authorize Google OAuth without providing different key/secret, and emails will be sent from the actual user.
Problem happens with the 2nd app (gTrax).
Authorization seems to work. Running the function inside the script to authorize lead to a screen asking for permission, gtrax then appears in the account as a registered app (could revoke access if needed).
But, when running the app, I get a message saying I need permission to do this action (UrlFetchApp / simple get)
My question is :
Is this possible that I need to register each user to get a key/secret for everyone (and dealing with that in the script)...
Or do OAuth can be registered with 1 key/secret ?
In other word, are (should) key/secret linked to a single user or are they only a kind of RSA-like key pairs that, when verified, can be used to authorize any user.
My understanding is this. When you use built-in Apps Script functions, like MailApp.sendEmail, the Google Apps Script "environment" takes care for you to ask authorization for the user (1st time he access your app) and save and manage the oAuth tokens for you, so it all runs smoothly.
When you call an external service using UrlFetchApp, Apps Script oAuth authorization process works differently. The authorization is just a strange popup you get on the script editor, when you actually make the fetch call. It is not processed at "compile time" and asked before you run anything like the other services. But you also do this step only once.
The "gotcha" is that this different authorization process does not work when a user is running the app as a webapp. AFAIK it only works from the script editor itself or running directly from a spreadsheet.
If your users are just a known few, you could advise everybody to open the script editor (or a spreadsheet that contains it) and run an specific function that will just attempt the UrlFetchApp.fetch call so the popup shows up and they authorize it. Once this step is done, they can use the webapp normally. Apps Script will do the magic for you after that.
But if you plan to share this broadly, say at the Chrome Web Store, and don't want to ask every user to do this somewhat strange step, then you'll need to manage all the authorization process yourself. It means, you'll have to register your app with the third party service (if it's Google's, it's at the API Console), where you will receive a client id and a client secret. With those you'll have to place a "Authorize" submit button on your app html that will redirect the users to the 3rd party authorization url, providing the correct scope, etc. When they authorize it, the 3rd party will redirect the user back to your app providing a code token as URL parameter. You'll use this code to call the 3rd party oAuth service to get the real access and possibly refresh tokens that you'll have to use on your UrlFetch calls. You'll be responsible to save these tokens, refresh them when they expire and so on. Not a very simple procedure :-/
Oh, and although your app have only one id and secret, the tokens are per user. Which makes sense, since each call you do must be on behalf of a specific user and he *must* have authorized it.
I hope this helps.