How do I properly proxy a request to the Google Maps Embed API?

2.5k views Asked by At

I'm attempting to use the Google Maps Embed API to embed a map matching an address that a user enters. Following the developer guide, I acquired an API Key to add to my API requests. I'm able to successfully pull up a map when I request it via the "src" attribute of an iframe in my React client, like so:

<iframe
   ...
   src={`https://www.google.com/maps/embed/v1/${mode}?key=${apiKey}&q=${encodedAddressQuery}`}
>

But this leaves my API key exposed to the client.

In Google's API Key Best Practices, it's recommended that the API key be stored in an environment variable and that a proxy server be used to safeguard keys. However when I try proxying the request (code below), it seems to return the appropriate map for a split second, but then the iframe replaces the map with an "Oops! Something went wrong" message, and the browser console displays this error:

Google Maps JavaScript API error: UnauthorizedURLForClientIdMapError https://developers.google.com/maps/documentation/javascript/error-messages#unauthorized-url-for-client-id-map-error Your site URL to be authorized: http://127.0.0.1:3001/api/maps?parameters=[my_encoded_parameters]

I'm just developing locally at the moment, so I've tried registering http://127.0.0.1:3001/* as an authorized URL as the error documentation suggests, and I've also tried removing all website restrictions on the API key just to see if I was authorizing the wrong URL, but both of those attempts still produced the same error.

I haven't found many resources on setting up a proxy other than this Google Codelabs project, but I haven't been able to pick anything out of it to help with this issue.

Here's the code I'm using in my React front end:

<iframe
   ...
   src={`http://127.0.0.1:3001/api/maps?parameters=${encodedAddressQuery}`}
>

And in my Express Node.js back end:

router.get('/api/maps', async (req: Request, res: Response) => {
    try {
        const parameters = req.query.parameters;
        const map = await MapDataAccessObject.getOne(parameters);
        return res.status(OK).send(map.data);
    } catch (err) {
        ...
    }
});

export class MapDataAccessObject {

    public static async getOne(parameters: string) {
        const apiUrl = this.getMapsEmbedUrl(parameters);
        const map = await axios.get(apiUrl);
        return map;
    }

    private static getMapsEmbedUrl(parameters: string) {
        const encodedParams = encodeURI(parameters);
        return `https://www.google.com/maps/embed/v1/place?key=${process.env.MAPS_API_KEY}&q=${encodedParams}`;
    };
}

I'm running my React front end and my Node.js back end in Docker containers.

My best guess is that I'm missing a request header that the Maps API is expecting, but I haven't found any documentation on that, or maybe I'm misunderstanding something more fundamental. Any guidance would be very much appreciated.

1

There are 1 answers

0
con On

There is no way to protect your google maps key on a website from being "spied on" because it is always public. The only way to protect your key from foreign usage is to restrict it's access to only the allowed domain/ip-addresses - so if it is used from someone else, it will not work or take anything from your credits (google will show an error message).

https://developers.google.com/maps/documentation/javascript/get-api-key