What Happens When Site Wants to Know Your Location - How I Voided My Firefox Warranty
Tags: geolocation, gps
Published: 2023-11-27 22:15:50

I have to admit ignorance that I’ve answered the `${site} wants to know your location` question many times without thinking very deeply about what it means, both in terms of privacy or how it works technically.

![Allow Location Access Prompt](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67m82qcde0h2bm7ya81g.png)

I’m guessing that sometime in the past 5 years you’ve seen it too, so what’s going on behind the scenes and how might you use this in developing applications?

---

## The Geolocation API

In order to make an application user-friendly, it makes good sense to know where the location of the user is. Things like recommended places can be made more relevant, navigation and routing can be started sooner, your items can be found more quickly, and the content you read or media you watch can be more personalized and adhere to applicable regulations (for instance gaming laws or media distribution rights).

If you try to solve this problem of determining the position **server-side**, the best indicator is generally the public IP address often reported in the [X-Forwarded-For](https://en.wikipedia.org/wiki/X-Forwarded-For) header of the HTTP request. When you are using a virtual private network (VPN) or a proxy server, that could make it look like you are 300 miles away from where you actually are. This location may be accurate enough for localization (l10n) and internationalization (i18n) use cases of country origin, but not much more than that.

You can solve it client-side too with the [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API) which is much more accurate. Published as a recommendation by the World Wide Web Consortium in 2013, the API allows us to write JavaScript code to fetch a position:

```javascript
navigator.geolocation.getCurrentPosition(function(position) {    
     console.log(position); 
});
```

You can try this in a web console yourself and get back latitude and longitude values along with an approximate accuracy expressed as the radius in meters.

![Web Console Location Output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/soim0tb0410zwnwm0wo9.png)

This makes sense for a mobile device that has a built-in GPS, but last time I checked I didn’t have a dongle for a GPS on my MacBook Pro. That made me curious about how browsers are implementing the spec.

---

## Void Your Firefox Warranty

Firefox has a number of configuration settings you can modify to customize your experience. By typing “about:config” instead of a URL you can identify the settings for `geo.wifi.uri` which points to a googleapis endpoint.

![Firefox Geolocate Service](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8je95suxc8fkw49attwz.png)

I wanted to see what these requests look like so started a local webserver with [http-echo-server](https://www.npmjs.com/package/http-echo-server) and updated the Firefox value of `geo.wifi.url` to point it to **http://localhost:8000** so I could watch the traffic.

```bash
$ npm install http-echo-server
$ http-echo-server
[server] event: listening (port: 8000)
[server] event: connection (socket#1)
[socket#1] event: resume
[socket#1] event: data
--> POST / HTTP/1.1
--> Host: localhost:8000
--> User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:60.0) Gecko/20100101 Firefox/60.0
--> Accept: */*
--> Accept-Language: en-US,en;q=0.5
--> Accept-Encoding: gzip, deflate
--> Content-Type: application/json; charset=UTF-8
--> Content-Length: 1198
--> Connection: keep-alive
-->
--> {"wifiAccessPoints":[{"macAddress":"81:3a:a9:88:80:58","signalStrength":-85},{"macAddress":"4c:97:6d:79:42:bf","signalStrength":-88},{"macAddress":"74:bc:0c:7f:0b:19","signalStrength":-84},{"macAddress":"00:a7:42:be:72:7f","signalStrength":-71},{"macAddress":"4c:66:6d:79:40:df","signalStrength":-85},
...]}
```

As you can see, Firefox is scanning the wireless local area network (WLAN) to identify the mac addresses nearby and received signal strength indication (RSSI) values and sending it all to a web service. If I make this request myself, I might get back a location like:

```javascript
{
  "location": {
     "lat": 20.6842849,
     "lng": -88.567782
  },
  "accuracy": 101.0
}
```

This is the data that feeds back into the Geolocation API we used earlier. You can learn more about this in the [Firefox docs](https://support.mozilla.org/en-US/kb/does-firefox-share-my-location-websites?redirectlocale=en-US&redirectslug=does-firefox-share-my-location-web-sites). Chrome does the same thing but doesn’t let you choose a different provider as far as I can tell.

## Positioning APIs

If I wanted to test my web app with a specific location, I could store this static response in a file called **test-location.json** and set the `geo.wifi.uri` to point to the file on disk instead of an http endpoint.

I’d like to see a more dynamic result though and started exploring the [HERE Positioning API](https://developer.here.com/documentation/positioning/topics/request-first-locate.html). The HERE Positioning API works in much the same way as the default, taking in **WLAN** data but it also supports other radio bands — GSM, LTE, WCDMA, Cell. The Positioning API responds with JSON that Firefox expects, but I’ll need to insert a man-in-the-middle to modify the body of the request. The following listing demonstrates how I accomplished that.

```javascript
// Use Express for the server
const express = require('express');
const request = require('request');
const app = express();

// Will need to parse and modify the body of the request with middleware
const parser = require('body-parser');
app.use(parser.urlencoded({extended: true}));
app.use(parser.json());

// Handler for a POST request from Firefox
app.post('/', function(req, res) {

    // Rewrite body for Positioning API request
    var body = {"wlan": []};
    req.body["wifiAccessPoints"].forEach(function(ssid) {
        body["wlan"].push({
            "mac": ssid["macAddress"],
            "powrx": ssid["signalStrength"]
        });
    });
    console.log(body);
  
    // Get your HERE APP_ID and APP_CODE from https://developer.here.com/projects
    // and set as environment variables to construct URL
    var app_id = process.env.APP_ID;
    var app_code = process.env.APP_CODE;
    var positioning_uri = `https://pos.api.here.com/positioning/v1/locate?app_id=${app_id}&app_code=${app_code}`
    console.log(positioning_uri);

    // Proxy to the HERE Positioning API
    request.post(positioning_uri, {json: true, body: body}, function(err, result) {
        if (err) { console.log(err) } else {
            console.log(result.body);
            res.status(result.statusCode).send(result.body);
        }
    });
  });

// Listen for Geolocation API requests from Firefox
var port = process.env.PORT || 8000;
app.listen(port, () => console.log(`Listening now on port ${port}`));
```

## Next Steps

By using a positioning API to find a precise location of a user, it is only a matter of some additional math to figure out whether or not any particular user is viewing content (such as streaming media) within a particular venue or geographic region. 

This can be a very important consideration for media distribution rights-holders or for activities that require adherence to local laws regarding gaming.