Forwarding OAuth 2 credentials from an authenticated request (in GCP specifically) - google-app-engine

I have an AppEngine application that is behind an IAP (identity-aware proxy), so it receives requests that are authenticated and include a JWT token. From the AppEngine application I want to make a request to the Google Sheets API. That also requires an authenticated connection, but given that I want that connection to be made under the same user that accessed the application via the IAP, does anyone know how to create a request from inside the AppEngine application that will forward the token to Google Sheets? Cannot find any information on the subject... I am using Java, so any Java pointers would be appreciated, but general/other language help is good too...

I will describe the 2 approach proposed in the comment
The first one, to reuse the IAP proxy token to access Google Sheet is impossible, and dangerous.
Impossible because you receive an identity token from IAP (at least the requester/browser send an identity token to IAP) and you need an access token to request Sheet APIs.
Dangerous because, if you are able to reuse the IAP token to request the Google Sheet, that means the user is authorized to access to the Google Sheet. And I'm sure that you build an app to prevent any direct access/modification to the Google Sheet.
The second one, is to use a technical account (typically a service account) and generate an access token to access the Sheet API.
This second approach is the best one (don't forget to correctly log the user request and the subsequent sheet API calls in your AppEngine app to have the end to end traceability). BUT, and it's for that you ask this question, it's impossible with the App Engine default service account.
In fact, to access to the Sheet API, you need to scope your access token with the Sheet API. Sadly, you can't do this with App Engine. You can do this with Cloud Run, Cloud Functions, Compute Engine (without the default service account, else you need an extra config to achieve this with the Compute Engine default service account). But not with App Engine.
So, you have 2 solutions:
Either you use another hosting platform (Cloud Run for example), but you loose the IAP capacity (for now)
You continue to use App Engine but you need to request an access token to another service account (it's not required to have a service account key file). You can use the Service Account Credential API for this. I wrote an article on this API
Note: later in 2021, App Engine should be able to accept custom service account, and thus the issue should be solved

Related

Sending authenticated HTTP requests from ComputeEngine to AppEngine using the default service account

For a project that I'm currently developing, I need to expose a servlet (hosted on Google App Engine) to a Java executable which is hosted on Google Compute Engine (in the same project). Such servlet performs some maintenance tasks, so it should never be triggered by non-authorized users. So, the goal is to authorize the requests coming from the Google Compute Engine instance that is running the JAR executable.
In the past I've solved the same issue by having the servlet exposed on HTTPS and rely on a "shared secret", known both to AppEngine application and to the JAR running on the Compute Engine instance. In that way, the instance calls the specific servlet (which is public), then the servlet verifies if the secret is correct, and if so, the request is allowed.
I don't like this approach. For sure we can do something better using challenge-response authentication or by using some other authentication procedure (probably via asymmetric crypto signing). However, this is not what I want to do.
My preferred way of acheiving the same result would be by using the Compute Engine Default Service Account. I am pretty sure there is a way of creating a HTTP POST request on the compute engine and authenticate that via the default service account key. Then, on the servlet, I would rely on the UserService to check whether the request is coming from the ComputeEngine default service account, and if so, I would accept that.
However, I have not seen any documentation or code example that explains how to do that. I suspect there might be possible to perform an authenticated HTTPRequest using the default Compute Engine Service Sccount (maybe adding the Bearer JWT token as Authorization header?).
Has anyone tried something like that?
You have several options; OAuth is likely your best bet.

appengine access via oauth2 python script (replacing ClientLogin)

I have an App Engine project which:
uses google.appengine.api.get_current_user() to handle users (and login:required)
has a URL to collect some data (which requires login)
has Google users but on a custom domain
I used to have a script to pull the data using the old https://www.google.com/accounts/ClientLogin interface, but now that interface is deprecated, I'm trying to work out what I need to do to get OAuth2 working to access my App Engine URL with a user value set.
I have worked my way through OAuth2 for devices to get myself an access key for my script (i.e. I can run it, authenticate in a web browser, then poll for the access key), as described in OAuth2 For Devices.
But I'm not sure:
what scope I should be using to request the access_token compatible with get_current_user(),
how to pass this in my request to App Engine so that it can create the the user header, and
whether I need to modify my app to use this access_token, eg adding callbacks etc
With regards to the last point, user was set by google's front end infrastructure so I’m hoping that that same infrastructure can somehow convert my OAuth access_token into a login name without me needing to update my app to do the callback part, because it should all be in appengine's infrastructure right and user is set before the request comes to my app.

Update google spreadsheet using python client API on GAE app

I've got a google spreadsheet owned by a GAE service account and I want my GAE Python app to update a cell in one of the rows.
Based on some reading, these are my findings:
the spreadsheets service is old-school. It's a Google Data API and most Google services are now on the Google API platform. For Google API services, one can use a service account to do two-legged oauth2 access, but not for Google Data API services. Oh, it seems one can do two-legged oauth on Google Data API services, but only if the app is on a Google Apps domain (which mine isn't)
I could implement a similar effect (i.e. a user of the app can use data in my spreadsheet and doesn't need to login or authorize in any way) by using my personal account. There's a complicated way that involves me to authorize the app once, store the token and reuse it when a user uses the app. There's another way, which is to use client login (i.e. I embed my personal login and password in the code and use it to authorize the app to access the data in my spreadsheet)
This latter approach seems fairly safe as well, but of course I must be very careful that my source code will not be exposed. The authorization is between the GAE app and the Google Data Spreadsheets API, so the actual user's machine is not involved at all.
My spreadsheet is owned by the service account and shared with my personal account.
Note that my app is also using the Google Drive API (to access some personal Drive files, also shared between me and the GAE service account), so for that it will authorize using the service account.
Can someone confirm that my findings are correct and this approach is sound?
You can use gdata.spreadsheets.client (Google Data API) on the OAuth2 (Google API platform) flow.
https://github.com/HatsuneMiku/googleDriveAccess
It uses 'oauth2client-gdata-bridge'.

Securing RESTful API in Google App Engine

I'm trying to figure out how to implement the following authentication flow:
The user accesses a web application (most likely to be written using Ruby on Rails) and authenticates (e.g., username/password).
The client consumes data via AJAX provided by a RESTful API built on Google App Engine (Python, webapp2).
Requirements:
Only users authenticated in the web application (Rails) should be able to access the API hosted on App Engine.
Users can have different roles in the web application (Rails), and the API (App Engine) needs to know what roles are associated to the given user to restrict access to certain data.
The client should be able to call the API (App Engine) directly via AJAX, without routing all requests through the web application (Rails).
I'm looking for suggestions on how to implement such workflow. Should I use OAuth (or OAuth2) for accessing the API? Should the OAuth provider live on App Engine and the web application (Rails) ask the API for a token on behalf of the user? If so, what is the best way to allow only the web application (Rails) to request OAuth tokens? Or should I consider a completely different strategy?
Any suggestions are greatly appreciated. I'm also looking for suggestions of libraries to implement OAuth in the context above.
I suggest you use caution if you are considering implementing an API built on the Google App Engine using OAuth for your security layer. I am currently involved in a project that is struggling to solve exactly this problem. The OAuth layer over the GAE is still new and considered by Google to be "experimental". Google's documentation is minimal at this point. What there is begins here. I wish you the best if you try to proceed, and I will do my best to offer help if you do.
My solution to this same problem was to write my own three-way authentication (like OAuth):
After the user is authenticated on the RoR server, it responds with a temporary token. This token is stored on the RoR server, is good for 60 seconds, and contains the user's roles.
The browser sends this token (using AJAX) to the webapp2 server. It's like logging in on that server using just the token.
The webapp2 server forwards the token on to the RoR server to make sure it is valid.
The RoR server makes sure the token hasn't expired and immediately deletes the token to prevent duplicate requests. If the token is valid, the RoR server responds with the user's roles.
If the response from the RoR server is good, the webapp2 server responds to the browser's AJAX call (in step 2) with a cookie indicating that this user is now logged in. The session should contain the user's roles.
Subsequent requests to the webapp2 server will include the cookie so that server can respond according to the user's roles.

How is the user authentication with Google accounts working inside the GAE technically

Applications that run inside the Google App Engine can use Google Accounts for user authentication. I already used this feature and it works great. I just want to know how this is working. Is there a HTTP cookie created? How can an application inside the GAE see that a user is logged in?
The AppEngine SDK takes care of the details for you, but essentially it generates the equivalent of an OAuth request to the Google Account service. All interactions with the login process go through the Google Account service (and thus the cookies it uses for session tracking are not available to the individual app).

Resources