2

My webapp loads Google Maps JS API v3 in the html head like this:

    <head>
    <script src="https://maps.googleapis.com/maps/api/js?key=MY_API_KEY&libraries=places"></script>
    </head>

Everything works fine, except in ~1 out of 100 mobile pageviews, Google Maps will make a callback to global gm_authFailure, which according to Google occurs when 'authentication fails' https://developers.google.com/maps/documentation/javascript/events#auth-errors

To debug, I'm capturing the entire console log from document load, and within my global gm_authFailure function:

  1. I'm printing the log, which just prior to gm_authFailure being called, always shows the below, which according to the docs, happens when Google determines the maps library wasn't loaded from https://maps.googleapis.com/maps/api/js:

    Google Maps JavaScript API error: NotLoadingAPIFromGoogleMapsError
    https://developers.google.com/maps/documentation/javascript/error-messages#not-loading-api-from-google-maps-error
    
  2. I'm also capturing the call stack that called gm_authFailure, using new Error().stack which outputs the below, which suggests a. the Maps library IS being loaded from maps.googleapis.com, and b. the error occurs at library authentication, rather than calling the library to use it.

    at gm_authFailure (https://EXAMPLE.COM/MY_PATH:3276:22)
        at https://maps.googleapis.com/maps-api-v3/api/js/40/7/common.js:73:368
            at Qp.o (https://maps.googleapis.com/maps-api-v3/api/js/40/7/common.js:149:177)
                at Object.c [as _tj1nu1] (https://maps.googleapis.com/maps-api-v3/api/js/40/7/common.js:67:84)
                    at https://maps.googleapis.com/maps/api/js/AuthenticationService.Authenticate?1shttps%3A%2F%EXAMPLE.COM%2FMY_PATH&4sMY_API_KEY&callback=_xdc_._tj1nu1&key=MY_API_KEY&token=18280:1:28
    

Hypothesis:

  1. Affected users have some app, browser extension, or cellular data saving feature that's blocking or caching the maps library and thus confusing the Maps library
  2. Is it strange that in the gm_authFailure callstack, the initiating 'https://maps.googleapis.com/maps/api/js/AuthenticationService.Authenticate' URL has my API key listed twice?

Things I know:

  1. When gm_authFailure is called, the library won't work anymore, e.g. I'm not able to create a new map:

    var test_map = new google.maps.Map(document.getElementById("test_google_map"), {"zoom":2})
    console.log(test_map.zoom)
    

    is undefined as opposed to 2

  2. Users that trigger this error often have other recorded pageloads on the same & different days that load without error.
  3. I'm unable to reproduce the error using the same device/browser & visiting the same page
  4. Even though this happens in only ~1% of pageloads, it's happening ~100 times/day, so I'm confident it's not a fluke.
Xiao Wei Chen
  • 103
  • 1
  • 11

1 Answers1

3

We fixed the error by this way:

Normally you insert the google maps script like this:

<script src="https://maps.googleapis.com/maps/api/js?v=quarterly&key=%YOUR_GOOGLE_MAPS_API_KEY%&libraries=geometry,places"></script>

After the change, replace the above script tag with a script:

    <script>
      (function () {
        // Some browsers caches the google maps script for a long time, which makes the token expired.
        // In order to solve this, we force the browser to refetch the script after 12 hours. This won't have too
        // much impact on performance, because the content of the script changes every day, so the cache is invalidated
        // anyway.
        var GOOGLE_MAPS_INVALIDATE_INTERVAL = 12 * 3600 * 1000;
        var timestamp = Math.floor(Date.now() / GOOGLE_MAPS_INVALIDATE_INTERVAL) * GOOGLE_MAPS_INVALIDATE_INTERVAL;

        var src = "https://maps.googleapis.com/maps/api/js?v=quarterly&key=%YOUR_GOOGLE_MAPS_API_KEY%&libraries=geometry,places&timestamp=" + timestamp;
        // Must use document.write. If we use document.body.appendChild, the main code (which depends on google map) might be executed before google maps is executed
        // TODO: we should finally make the geoUtils to be an async function, that only resolves when google maps is ready.
        document.write('<script src="'+src + '">\u003c/script>'); // if not using unicode escape (\u003c), browser will think the outside script is closed.
      })();
    </script>

Explanation:

Every time the error happens, the user seems to have visited the page one or several days ago.

According to NotLoadingAPIFromGoogleMapsError even when loading correct URL, google maps api's session lasts for 24 hours. And NotLoadingAPIFromGoogleMapsError will happen after the session expires.

The request to https://maps.googleapis.com/maps/api/js/AuthenticationService.Authenticate contains a param "token=xxx". When the token is incorrect, the error happens.

The token's value is computed out from the request url and some magic numbers, which are defined in the script loaded from https://maps.googleapis.com/maps/api/js (the initial script to load google maps). The magic numbers in the script is changed each day.

After the user left the page and came back, the browser seems to do a "fast recovery", which will load the script content from cache, instead of respecting cache-control. But the magic number in the cache is expired, so the error happens.

But if we replace the script tag with some code with timestamp, we can make sure the script content is reload after the "fast recovery".

lukeupup
  • 75
  • 1
  • 7