Search code examples
firebase-authenticationgoogle-signinfirebaseuiactions-on-googlegoogle-home

"Cannot parse context" error using firebaseui-web with Google Home


I am trying to setup account linking for my Google Home action. To authenticate the user when they login to do the linking, I am using Firebase Auth and FirebaseUI-web with the Google Sign-In provider. The page and code-flow validates when using the Google OAUth2 Playground and when I test account linking using the Google Home emulator, both of which I've tested using desktop Chrome. But when I go to test with the Google Home device and app, it starts the flow and shows the login button, lets me select an account, redirects back to my page, and then generates an error:

An internal error has occurred. Dismiss

When I connect a Chrome debugging console to it, I have the following error in the console:

[  1.995s] [firebaseui] Internal error: {"error":{"errors":[{"domain":"global","reason":"invalid","message":"Cannot parse context"}],"code":400,"message":"Cannot parse context"}}

If I look at the network log, the last xhr POST returns a 400 error. (Some information redacted, since I don't know the sensitivity of the data being sent.)

Request:


URL:https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyAssertion?key=[redacted]
Method:POST

Request headers:


Content-Type:application/json
Origin:https://redacted.example.com
Referer:https://redacted.example.com/auth?response_type=code&client_id=api-ai-test&redirect_uri=https://oauth-redirect.googleusercontent.com/r/redacted-project&scope=test1&state=redacted
User-Agent:Mozilla/5.0 (Linux; Android 6.0.1; A0001 Build/MHC19Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36
X-Client-Version:Chrome/JsCore/3.6.5

Body:


{
  "postBody": "id_token=redacted&access_token=re.dacted&providerId=google.com",
  "requestUri": "https://redacted.example.com/auth?response_type=code&client_id=api-ai-test&redirect_uri=https://oauth-redirect.googleusercontent.com/r/redacted-project&scope=test1&state=redacted",
  "returnIdpCredential": true,
  "returnSecureToken":true
}

HTTP response code is 400 with body:


{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "invalid",
    "message": "Cannot parse context"
   }
  ],
  "code": 400,
  "message": "Cannot parse context"
 }
}

Is there any way to get this to work correctly? What is causing the problem?

Update to better explain user flow

It is important to understand that FirebaseUI is being used to authenticate a user logging into my OAuth2 service. Not to get OAuth information to provide to Home directly.

So the flow is:

  1. User initiates the action on Home with a trigger such as "Hey Google, talk to bill pay by voice"
  2. Since the user hasn't connected their Home to the "bill pay by voice" service yet, Home prompts the user to go to the Google Home app and link the two accounts.
  3. That card shows up like this. enter image description here
  4. The user clicks on the card and a Chrome custom tab is opened with my login page, letting them log into my service. This page is implemented with FirebaseUI and has a single button letting them log into my service using their Google account.
  5. If necessary, they select an account, log in, and are redirected back to the page to continue. It is at this point that the error appears.
  6. If it was working, however, the page would send my back-end service the login information. This would end the involvement of FirebaseUI. At this point, the backend service would contact the Firebase Auth service to get the account information, generate an authorization code for this account, and have the page continue the OAuth2 flow and provide an authorization code from my service to Google.

Note that although (4) takes place in a Chrome custom tab, I've also told it to open in Chrome itself (through the menu item in the tab), and it continues to show the same problem.

It isn't clear to me exactly what is going on in step (5). There are some calls to the identity toolkit service that seem to work before it fails with the call indicated.


Solution

  • Ok. I think this is what is happening. FirebaseUI signInWithCredential used to sign in with an OAuth token calls verifyAssertion underneath. That passes the Google OAuth access token and ID token to the Firebase Auth backend along with the requestURI (the current URL).

    The problem is you are trying to build your own OAuth provider using FirebaseUI. OAuth protocol recommends that the state parameter be provided by the 3rd party app (google home) and then returned back to it along with the Auth code. The problem is this state parameter in your current URL is being sent to Firebase Auth backend in the verifyAssertion (requestUri field) request which then tries to check it and notices it is invalid (this is not meant to be consumed by Firebase Auth backend in this case), thus throwing the error "Cannot parse context" which is thrown when there is an OAuth state parameter mismatch.

    To get this to work, the webpage where your login UI is hosted should not contain a state parameter in the query string.

    For now here is one option to unblock you:

    1. Google Home triggers account linking, redirects to your web page with all the expected parameters (redirect_uri, scope, client_id, state, response_type).
    2. Save these in sessionStorage and redirect to another page that hosts your FirebaseUI.
    3. Using the sessionStorage info, render firebaseUI and all additional UI and when the suer finishes the flow, the user is returned to the login page.
    4. You can then issue an auth code, pull the sessionStorage info (redirect_uri, state) and redirect to redirect_uri with state and auth code appended.

    The summary is that you need to ensure that when user is redirected back to the page that hosts your firebaseui after sign in, no state parameter is available in the URL query string. Otherwise the Firebase Auth backend will assume it was meant for it.

    Test the above and let me know if this works. I will get back to you next week on this issue.

    Please let me know if this is not clear, I can explain further if needed.