2

I access a Google calendar using a service account. This means that the owner of the calendar will not be prompted for his authorization.

This works fine in Python, where I make a requests call to https://accounts.google.com/o/oauth2/token with a specific body. This gets me back a token I can use later.

I now need to have an application running in a standalone browser (Chrome - without user interaction) and tried to directly port this call as

fetch('https://accounts.google.com/o/oauth2/token',
    {
        method: 'POST',
        body: JSON.stringify(body),
    })
    .then(function(res) { return res.json(); })
    .then(function(data) { alert(JSON.stringify(data)) })

but I get a reply from Google

Failed to load https://accounts.google.com/o/oauth2/token: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://devd.io' is therefore not allowed access. The response had HTTP status code 400. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

My limited understanding of CORS (a previous answer was a very good read) is that

No 'Access-Control-Allow-Origin' header is present on the requested resource

means that it is not present in the response headers, which means that Google does not want me to access its resources via JS when ran from the browser (which points to something else that https://accounts.google.com).

This may be a good idea but I control all elements, from the code to the browser and would like to get that token the same way I get it in a non-browser environment, specifically my working Python code.

How can I tell https://accounts.google.com to send me back a Access-Control-Allow-Origin header which tell my browser that it is OK to accept the call?

WoJ
  • 27,165
  • 48
  • 180
  • 345
  • See https://stackoverflow.com/questions/43276462/cors-issue-while-requesting-access-token-google-oauth-2/43276710#43276710 – sideshowbarker Oct 18 '17 at 21:51
  • @sideshowbarker: *"OAuth2 auth endpoint doesn't support AJAX by design"* -- I do not understand that point. AJAX, from the prespective of the server, is just a HTTP call. It **does** support non-interactive authentication (and the associated authorization for a given scope) - this works fine in my server code mentionned in the question. – WoJ Oct 19 '17 at 06:03

2 Answers2

5

You can't.

Client side and server side code need to interact with OAuth in different ways.

Google provide documentation explaining the client side process.

Importantly, part of it involves redirecting to Google's servers instead of accessing them with fetch or XMLHttpRequest.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • What I do not understand is that from python I can either make a call which requires a user authorisation (by copy paste a link received as a response) **OR** make a service account type of call where interactive authorization is not needed (it is provided to the service account upstream). Why not in JS? The context is the same (a human - less access to data) and the same rights for the requesting account. – WoJ Oct 18 '17 at 20:28
  • I read the link you provided, it is great and very similar to the one in the python version of the docs (where explicit user authorisation may also be an option) – WoJ Oct 18 '17 at 20:31
  • Moreover, the security implications are none - I will end up writing a python layer which will request, service account style, the data and provide them verbatim to my JS code. This could have happened directly in JS (I am not whining, just trying to understand the rationale) – WoJ Oct 18 '17 at 20:35
  • In JS you can't hide the authorisation details from the user - so you'd have to provide the secret key etc. in the JS code. And since anyone using your page (including a bot) can view the browser code and debug the variable values, your secret key is then no longer secret. So that's why you can't use a service account from JS, as I understand it. Whereas Oauth is fine in JS because you ask the user themselves for _their_ credentials, not yours. – ADyson Oct 18 '17 at 20:40
  • @ADyson: these are fair points - but in my case I control the code and the browser (thi sis a kiosk or dashboard kind of application). – WoJ Oct 19 '17 at 06:04
  • @WoJ — Browsers are built with the assumption that that is not the case. – Quentin Oct 19 '17 at 08:33
  • @WoJ Are you 100% certain the kiosk mode can never be turned off by the user, and that they can't simply hit F12 or Ctrl+U still, to see the page's details? And how will you stop people from visiting your site through a non-kiosk browser? Is the site on a completely locked-down intranet, with the server access whitelisted to certain IP addresses, something like that? – ADyson Oct 19 '17 at 08:45
  • @ADyson: yes. This is my home network, under complete control. I guess that, as mentioned byvQuentin, a browser is not the best tool for what I want to achieve (unfortunately there are no others alternatives AFAIK). I will go for the API intermediate layer. – WoJ Oct 19 '17 at 17:56
1

@Quentin's answer "You can't" is the right one for my question ("how can I force the server to send back the right header").

This is a decision at Google not to provide this header, effectively cutting off any non-interactive applications.

As a solution, I will look at

  • how to force the browser not to take into account the security mechanisms provided by CORS (there seems to be some ways through extensions or command-line arguments, I will update this answer once I find it)
  • or write an intermediate layer which will query the data for me and pass them verbatim to the application (this is equivalent, in my case, of just making the query from JS - but it adds an extra layer of code and server)
WoJ
  • 27,165
  • 48
  • 180
  • 345