Start ~2wks ago a few customers have started hitting an issue that prevents them from authenticating with Google services. All of the instances that I have encountered so far appear to be on non-gmail domains. The issue appears to be (see differences section below) that the https://accounts.google.com/o/oauth2/auth request from gapi.auth.authorize is returning a response with the 'X-Frame-Options: SAMEORIGIN' header for these particular clients. I have been unable to reproduce this issue locally, but was given a HAR of the failed request.
This same authentication method is working well for a variety of other clients including other hosted domains (non-@gmail accounts).
Any thoughts on what might be causing this request to fail? Further things to investigate or additional information?
In the developer console https://www.moo.do is a valid Javascript Origin.
Ultimately the error displayed in the user's console: Load denied by X-Frame-Options: https://accounts.google.com/o/oauth2/auth? does not permit cross-origin framing.
Similarities
Differences
Below is a successful and a failed request. I'm at a loss for why the failed request is returning the extra header. Some of the information has been removed ([REMOVED]) or redacted (XXXX/YYYY).
Successful Request
{
"startedDateTime": "2016-03-03T15:52:27.625Z",
"time": 84.7660000436008,
"request": {
"method": "GET",
"url": "https://accounts.google.com/o/oauth2/auth?client_id=597847337936.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.install%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.appdata%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcontacts.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&immediate=true&login_hint=YYYYY%40YYYY.com&authuser=-1&include_granted_scopes=true&proxy=oauth2relay593501023&redirect_uri=postmessage&origin=https%3A%2F%2Fwww.moo.do&response_type=token&state=867674703%7C0.1520984533&jsh=m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.en.d1w1l2mcNcs.O%2Fm%3D__features__%2Fam%3DAQ%2Frt%3Dj%2Fd%3D1%2Frs%3DAGLTcCMuer-UxvQzEv7JYzkFSQh2Kou7xA",
"httpVersion": "unknown",
"headers": [
{
"name": "pragma",
"value": "no-cache"
},
{
"name": "accept-encoding",
"value": "gzip, deflate, sdch"
},
{
"name": "accept-language",
"value": "en-US,en;q=0.8"
},
{
"name": "upgrade-insecure-requests",
"value": "1"
},
{
"name": "user-agent",
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36"
},
{
"name": "accept",
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
},
{
"name": "cache-control",
"value": "no-cache"
},
{
"name": ":authority",
"value": "accounts.google.com"
},
{
"name": "cookie",
"value": [REMOVED]
},
{
"name": ":scheme",
"value": "https"
},
{
"name": "x-chrome-connected",
"value": "id=108229145437218213687,mode=0,enable_account_consistency=false"
},
{
"name": "referer",
"value": "https://www.moo.do/app/"
},
{
"name": "x-client-data",
"value": "CKO2yQEIwbbJAQj9lcoB"
},
{
"name": ":method",
"value": "GET"
}
],
"queryString": [
{
"name": "client_id",
"value": "597847337936.apps.googleusercontent.com"
},
{
"name": "scope",
"value": "https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.install%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.appdata%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcontacts.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive"
},
{
"name": "immediate",
"value": "true"
},
{
"name": "login_hint",
"value": "YYYYY%40YYYY.com"
},
{
"name": "authuser",
"value": "-1"
},
{
"name": "include_granted_scopes",
"value": "true"
},
{
"name": "proxy",
"value": "oauth2relay593501023"
},
{
"name": "redirect_uri",
"value": "postmessage"
},
{
"name": "origin",
"value": "https%3A%2F%2Fwww.moo.do"
},
{
"name": "response_type",
"value": "token"
},
{
"name": "state",
"value": "867674703%7C0.1520984533"
},
{
"name": "jsh",
"value": "m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.en.d1w1l2mcMcs.O%2Fm%3D__features__%2Fam%3DAQ%2Frt%3Dj%2Fd%3D1%2Frs%3DAGLTcCMuer-UxvQzEv7JYzkFSQh2Kou7xA"
}
],
"cookies": [
{
"name": "LSOLH",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "SMSV",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "RMME",
"value": "false",
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "ACCOUNT_CHOOSER",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "GALX",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "GoogleAccountsLocale_session",
"value": "en",
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "expor",
"value": "3100077",
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "GMAIL_RTT",
"value": "151",
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "S",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "SID",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "LSID",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "HSID",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "SSID",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "APISID",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "SAPISID",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "GAPS",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "LSOLH",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "OGPC",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
},
{
"name": "NID",
"value": [REMOVED],
"expires": null,
"httpOnly": false,
"secure": false
}
],
"headersSize": -1,
"bodySize": 0
},
"response": {
"status": 200,
"statusText": "OK",
"httpVersion": "unknown",
"headers": [
{
"name": "pragma",
"value": "no-cache"
},
{
"name": "date",
"value": "Thu, 03 Mar 2016 15:52:27 GMT"
},
{
"name": "content-encoding",
"value": "gzip"
},
{
"name": "x-content-type-options",
"value": "nosniff"
},
{
"name": "server",
"value": "GSE"
},
{
"name": "content-language",
"value": "en"
},
{
"name": "status",
"value": "200"
},
{
"name": "cache-control",
"value": "no-cache, no-store, max-age=0, must-revalidate"
},
{
"name": "content-type",
"value": "text/html; charset=UTF-8"
},
{
"name": "alt-svc",
"value": "quic=\":443\"; ma=2592000; v=\"30,29,28,27,26,25\""
},
{
"name": "alternate-protocol",
"value": "443:quic,p=1"
},
{
"name": "x-xss-protection",
"value": "1; mode=block"
},
{
"name": "expires",
"value": "Fri, 01 Jan 1990 00:00:00 GMT"
}
],
"cookies": [],
"content": {
"size": 2096,
"mimeType": "text/html"
},
"redirectURL": "",
"headersSize": -1,
"bodySize": -1,
"_transferSize": 1051
},
"cache": {},
"timings": {
"blocked": 1.07300002127886,
"dns": -1,
"connect": -1,
"send": 0.39199995808303,
"wait": 81.3200001139194,
"receive": 1.9809999503195002,
"ssl": -1
},
"connection": "2025013",
"pageref": "page_1"
}
Failed Request
{
"startedDateTime": "2016-03-03T10:12:35.752Z",
"time": 442.6579999853857,
"request": {
"method": "GET",
"url": "https://accounts.google.com/o/oauth2/auth?client_id=597847337936.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.install+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.appdata+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcontacts.readonly+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.readonly+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly&immediate=true&login_hint=XXXXX%40XXXX.com&authuser=-1&include_granted_scopes=true&proxy=oauth2relay235542267&redirect_uri=postmessage&origin=https%3A%2F%2Fwww.moo.do&response_type=token&state=638324187%7C0.1211244794&jsh=m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.de.7pJmZpTVQp8.O%2Fm%3D__features__%2Fam%3DAQ%2Frt%3Dj%2Fd%3D1%2Frs%3DAGLTcCOmU_zLoubGrUI-_ZI9ZhB7rGP1Sw",
"httpVersion": "unknown",
"headers": [
{
"name": "Accept",
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
},
{
"name": "Referer",
"value": "https://www.moo.do/app/"
},
{
"name": "Upgrade-Insecure-Requests",
"value": "1"
},
{
"name": "User-Agent",
"value": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36"
}
],
"queryString": [
{
"name": "client_id",
"value": "597847337936.apps.googleusercontent.com"
},
{
"name": "scope",
"value": "https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.install+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.appdata+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcontacts.readonly+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.readonly+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly"
},
{
"name": "immediate",
"value": "true"
},
{
"name": "login_hint",
"value": "XXXXX%40XXXX.com"
},
{
"name": "authuser",
"value": "-1"
},
{
"name": "include_granted_scopes",
"value": "true"
},
{
"name": "proxy",
"value": "oauth2relay235542267"
},
{
"name": "redirect_uri",
"value": "postmessage"
},
{
"name": "origin",
"value": "https%3A%2F%2Fwww.moo.do"
},
{
"name": "response_type",
"value": "token"
},
{
"name": "state",
"value": "638324187%7C0.1211244794"
},
{
"name": "jsh",
"value": "m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.de.7pJmZpTVQp8.O%2Fm%3D__features__%2Fam%3DAQ%2Frt%3Dj%2Fd%3D1%2Frs%3DAGLTcCOmU_zLoubGrUI-_ZI9ZhB7rGP1Sw"
}
],
"cookies": [],
"headersSize": -1,
"bodySize": 0
},
"response": {
"status": 200,
"statusText": "OK",
"httpVersion": "unknown",
"headers": [
{
"name": "pragma",
"value": "no-cache"
},
{
"name": "date",
"value": "Thu, 03 Mar 2016 10:12:35 GMT"
},
{
"name": "content-encoding",
"value": "gzip"
},
{
"name": "x-content-type-options",
"value": "nosniff"
},
{
"name": "server",
"value": "GSE"
},
{
"name": "x-frame-options",
"value": "SAMEORIGIN"
},
{
"name": "content-language",
"value": "de"
},
{
"name": "status",
"value": "200"
},
{
"name": "cache-control",
"value": "no-cache, no-store, max-age=0, must-revalidate"
},
{
"name": "content-type",
"value": "text/html; charset=UTF-8"
},
{
"name": "alt-svc",
"value": "quic=\":443\"; ma=2592000; v=\"30,29,28,27,26,25\""
},
{
"name": "alternate-protocol",
"value": "443:quic,p=1"
},
{
"name": "x-xss-protection",
"value": "1; mode=block"
},
{
"name": "expires",
"value": "Fri, 01 Jan 1990 00:00:00 GMT"
}
],
"cookies": [],
"content": {
"size": 0,
"mimeType": "text/html"
},
"redirectURL": "",
"headersSize": -1,
"bodySize": -1,
"_transferSize": 0,
"_error": ""
},
"cache": {},
"timings": {
"blocked": 0.944999977946281,
"dns": -1,
"connect": -1,
"send": 0.3190000134054589,
"wait": 151.53400000417625,
"receive": 289.85999998985767,
"ssl": -1
},
"pageref": "page_1"
}
How to fix error Refused to frame '<URL>' because it violates the following Content Security Policy directive: frame-src / default-src / child-src. Correction of the error «Refused to frame '<URL>'» is obvious - you need to add the blocked host-source from '<URL>' to the directive, in which it blocks.
Great.
Problem/Cause
The Google authorization servers attach the 'X-Frame-Options: SAMEORIGIN' header to Hosted Domain accounts (Google Apps) if the app makes a request for more than 7 OAuth scopes. Less than 7 (it doesn't matter what the scopes are) and the same request on the same account does not have the X-Frame-Options header specified in the return call.
There are additional moving parts that are required to make this repro happen (must supply the jsh param from the GAPI JS client) and other scenarios where the X-Frame-Options header is returned. At this point however the repro files show that there appears to be an issue with the Google authorization server.
Complaining :)
Due to the nature of the X-Frame-Options header, client-side error detecting will not know that a request has been blocked which makes this particular error even more of a problem. Additionally the authorization callback will never be notified that there was any kind of failure leaving the requesting app in a state of limbo waiting for any kind of notification.
Problem Demonstration
Demos
There are two repro files included:
auth_repro.html - This entirely sidesteps the GAPI JS client and demonstrates the issue. It does use a specific param (jsh) that the client attaches to the authorization requests to make the problem happen.
auth_repro_gapi.html - This uses the GAPI JS client to reproduce the issue.
Solution
Don't be lazy about trimming/managing scopes that you are requesting or your authorization requests will start silently failing.
It would also be great if this behavior wasn't a thing. Best guess is that it's a security measure gone wrong?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With