App Engine IAP Authentication failing on AJAX requests to my deployed server

1,336 views
Skip to first unread message

Devin Dykhoff

unread,
May 7, 2018, 3:52:47 PM5/7/18
to Google App Engine
It feels like there's a missing link here, but I've followed the IAP docs to the letter for setting up IAP authentication in my app and I'm stilling running into issues, so here we go..

I have a Node.js application deployed in the App Engine flexible environment. It serves some basic content using handlebars and services a RESTful API using express. Everything client side appears to work as it does on my local dev system (pages load, IAP authentication works as intended allowing only the whitelisted users). The server side verifies the JWT provided by IAP utilizing signed headers. The problem comes in when I POST (via fetch) to my server in App Engine. The request gets 302 redirected to a Google oauth URL which then consequently fails with HTTP code 405 because of the preflight method OPTIONS not being supported by the oauth endpoint. I'm also seeing a duplicate request sent to my API endpoint that is duplicated except the method is GET and the payload is stripped, which also returns code 302 (this is part of the preflight I imagine)

Why is my request to my own server being redirected to an oauth endpoint? This is never mentioned in the docs. Do I need to handle this case, or is there CORS configurations that I need to account for?

The entire error method I see is here:

Failed to load https://accounts.google.com/o/oauth2/v2/auth?<headers>: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://<my_project>.appspot.com' is therefore not allowed access. The response had HTTP status code 405. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Any help would be appreciated...

Attila-Mihaly Balazs

unread,
May 8, 2018, 12:02:51 AM5/8/18
to Google App Engine
AFAIK Urlfetch works just like any external client (so just like using cURL from your machine), so if it isn't authenticated, it will be redirected to the authentication page.

If you don't need synchronous communication, you can take a look at taskqueues: https://cloud.google.com/appengine/docs/standard/python/taskqueue/ - AFAIK it is not GA in the Flexible environments but you can take part in the Cloud Tasks alpha: https://docs.google.com/forms/d/1g6yRocQ3wtdTArfO4JX8DoqOhYmsoTVgrlFnS0mV1bo/viewform?edit_requested=true

Attila

Devin Dykhoff

unread,
May 8, 2018, 8:43:33 AM5/8/18
to Google App Engine
Hi Attila, 

Thanks for the reply. Are you saying that I do need to implement OAuth into my app? The part I am confused about is how it interacts with IAP, is that not already performing OAuth on my application? 

Thanks for the suggestion for task queue but that does not help me in my use case.

Devin

Jordan (Cloud Platform Support)

unread,
May 8, 2018, 3:09:07 PM5/8/18
to Google App Engine
As described in the documentation, Cloud IAP is used for securing your applications with Google OAuth from all incoming requests (this includes external clients, and your own application as Attila mentioned).

Since your application cannot follow the OAuth redirect to login, it is therefore recommended to use Service Account authentication when making requests via code from your own application. 

As for your client-end AJAX sessions, you can follow the Managing Cloud IAP sessions to properly handle AJAX session refreshes.  

- Note: Google Groups is reserved for general product discussions and is not for technical support. For further technical support in coding your application with Cloud IAP, it is recommended to post your detailed questions to Stack Exchange using the supported Cloud tags. 

Devin Dykhoff

unread,
May 8, 2018, 3:23:35 PM5/8/18
to Google App Engine
Jordan, thanks for the suggestion to user a service account. I will try implementing that. Will the user information still be present in the signed JWT headers as described in these docs if I use a service account for subsequent requests? The issue cannot be related to session refreshes as it occurs immediately after refreshing the page. I have reviewed that page previously and will address that issue separately in my app. I also have a question open on Stack Exchange here.

Jordan (Cloud Platform Support)

unread,
May 9, 2018, 10:20:44 AM5/9/18
to google-a...@googlegroups.com
The claim set (found under the 'HTTP/REST' tab) of the Service Account JWT is identical to that of the claim set required by Cloud IAP Signed Headers; the only difference being ' email' is 'iss' for the service account email address. 

So once you decode the JWT, if you are missing the 'email' claim you know it is a program making a request via a service account, and to check the 'iss' claim for the service account email. 

Devin Dykhoff

unread,
May 9, 2018, 12:08:44 PM5/9/18
to Google App Engine
That does not seem like the same thing to me. I'd be expecting the authenticated user's credentials as part of the JWT identity, not the service account. It is the user's actions on the application triggered the API calls after all. To get this I would need to implement an implicit OAuth flow if I understand this correctly. Can I utilize the already obtained token from IAP OAuth to authenticate AJAX requests on behalf of the user?

Jordan (Cloud Platform Support)

unread,
May 9, 2018, 2:24:03 PM5/9/18
to Google App Engine
You should indeed be able to reuse the user token provided in order to make subsequent requests to IAP from your own app. Note that when that token expires you would need to follow the suggested 'Managing Cloud IAP sessions' guide to refresh the token. 

You can of course not use IAP and implement your own authentication (doesn't even have to be Google OAuth, e.g your own login screen and user database). This would free up your application to make self-authenticated requests to itself (however you choose to do so), and use IDs in URL parameters or headers to pass user information like email addresses. 

- Note: for all further technical support in authenticating and coding your application to fit your needs, it is recommended to post further additional detailed questions to Stack Exchange using the supported Cloud tags. 

Devin Dykhoff

unread,
May 9, 2018, 3:59:31 PM5/9/18
to Google App Engine
For the record, the solution to my issue was what I initially suspected: the IAP authorization process does in fact allow AJAX requests to access other IAP protected resources (e.g. my backend API in this example). The issue was related to the `fetch()` method on the front end client, which by default does not send cookies with the HTTP requests. You must pass `credentials: 'include'` in the options to fetch. 

By default, fetch won't send or receive any cookies from the server, resulting in unauthenticated requests if the site relies on maintaining a user session (to send cookies, the credentials init option must be set).

Attila-Mihaly Balazs

unread,
May 10, 2018, 10:48:25 AM5/10/18
to Google App Engine
Hi Devin,

I'm happy that you managed to solve the issue! Sorry for my misleading messages, I was thinking that you were trying to fetch from the server side using the UrlFetch service (https://cloud.google.com/appengine/docs/standard/python/issue-requests) :-)

All the best,
Attila
Reply all
Reply to author
Forward
0 new messages