Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google OAuth gapi.auth.authorize X-Frame-Options: SAMEORIGIN

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

  • Both of these requests use immediate=true when authorizing. When using immediate=false (which causes the request to go through the account selector popup) the failure account successfully services the request).

Differences

  • In the response to the failure account there is an X-Frame-Options header.
  • In the response to the failure account the response.content.size field is 0. Additionally the response._transferSize is 0 and there is a response._error field present (its empty).
  • In the response of the failure account the scopes param is encoded as '[scope]+[scope]+[scope]' which has been deprecated. [Edit: Received another HAR that uses the properly non-deprecated space separated scopes that still fails]

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"
}
like image 399
Grant Watters Avatar asked Mar 03 '16 17:03

Grant Watters


People also ask

How do you fix a refused frame?

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.


1 Answers

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?

like image 148
Grant Watters Avatar answered Oct 05 '22 22:10

Grant Watters