Data Privacy in ConnectStats

I recently saw a negative review on the App Store for ConnectStats warning users that ConnectStats is not a Garmin app and therefore people should avoid giving away login information in the app as the data may get stolen.

Keeping data secure both on your phone or online has been a key guiding principle in how I tried to implement the app. So while I understand the concern, I felt it was a bit unfair.

I have been careful to make sure the data isn’t shared and the passwords are never sent to me. I also made the app open source so that people can check for themselves what it is doing.

I felt it may be worth to write a bit of details on what is ConnectStats doing with your data and password, with link to the code. So people can either let me know if I miss something or feel better about using the app.

Your login information

Garmin Website service

The app can connect directly to the Garmin website to retrieve information. In order to do that it needs to have access and store your username and password. To do that it stores the password in the keychain of your phone and never locally in a way someone looking at the files saved from connectstats could retrieve. It then relies on the iPhone keychain mechanism which Apple can ensure is secure. The key file to look at to see how it’s done is GCAppPasswordManager.

Strava and Withings services

For the Strava and Withings service, the authentication process uses the OAuth 2.0 so the password is never even seen by ConnectStats. The library I use to manage the OAuth 2.0 is provided by Google , and you can see in the file used by connectstats how the tokens are retrieved from the keychain in this file for Strava for example via this call:

                                                                   BOOL didAuth = [GTMOAuth2ViewControllerTouch authorizeFromKeychainForName:keyChainNameauthentication:auth error:&error];

ConnectStats service

ConnectStats also maintains a service that can receive the fit file from the new Garmin Health API. In that case the the authentication is done via OAuth 1.0a. So the passwords are never seen by ConnectStats or its web server, but only tokens are exchanged with Garmin. These tokens are then saved into the keychain of your phone as well as on the database in the server. Note that the server is open sourced as well. While the server is open sourced, the configuration files containing the database passwords and other secret keys are only saved on the server and not in the code. The website is hosted on Godaddy, a reputable company, and I rely on their security to make sure the access to the website is secured.

Your Activities Data

On the phone

Your activity data is kept on the phone and stored locally. So it will be as secure as you keep your phone. You can also see that if you try to run the app in airplane mode all the browsing of statistics and downloaded data will work. Of course you need a connection to download new data…

On connectstats server

With the new Garmin Health API service, ConnectStats needs to maintain a database in a server containing your activities. This is the case if you choose in the app Garmin as a service and source to be All or ConnectStats. If you choose Garmin WebSite only then the data will be accessed directly from the Garmin servers. Note that this is not the officially supported method from Garmin, can and has been subject to outage in the past due to undocumented changes to their website.

If the data is stored on the ConnectStats server, the access to that data is done via an OAuth 1.0 process. Both the app and the server keep a secret token, and use that to do the authentication. The tokens are provided by Garmin, so in order to access your data you will need to do a successful login on the Garmin service and obtain the token this way.

ConnectStats does not maintain any types of user name or its own passwords/user system, which means the data stored on the server can not be traced back to you. Everything is linked and identified by the sha1 hash tokens obtained by Garmin, which look something like this aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d.

The only person with access to the database with your data is myself, and no one else helps me or has access to the login information. If that ever changes, I’ll make sure to talk about it in this blog.

Note that if you use the source for Garmin data to be both the website AND connectstats service (what I recommend), you will need to enter your login details in the app (as in the Garmin website section), but that data will stay on your phone in your keychain. It will never be uploaded to the connectstats server. So on the server it will still be impossible to link the data saved in the database to your Garmin user account, email or username as well.

Bug Reports

When you send a bug report, this will send the log information, which will look something like the below. This is mostly information that helps me see what has happened and try to understand the problems. You can see in the code everything that is logged by looking for calls to the function RZLog. No sensitive information, like password is logged.

2020-01-19 09:47:25.588 46878:407 - INFO:GCAppDelegate.m:132:-[GCAppDelegate application:didFinishLaunchingWithOptions:] =========== ConnectStats 5.4.1.0 ==== iOS 13.3 Xcode 11 x64 ===============
2020-01-19 09:47:25.679 46878:407 - INFO:GCActivityTypes.m:156:-[GCActivityTypes loadPredefined] Registered 90 ActivityTypes
2020-01-19 09:47:25.681 46878:407 - INFO:GCAppDelegate.m:554:-[GCAppDelegate settingsUpdateCheck] Current Version 5.4.1.0 first seen 2020-01-14 19:49:58 +0000 (9 total versions)
2020-01-19 09:47:25.829 46878:407 - INFO:GCAppDelegate.m:273:-[GCAppDelegate applicationDidBecomeActive:] active
2020-01-19 09:47:25.844 46878:407 - INFO:GCActivityListViewController.m:287:-[GCActivityListViewController viewDidAppear:] forceTouch Enabled
2020-01-19 09:47:25.847 46878:407 - INFO:GCAppDelegate.m:650:-[GCAppDelegate startSuccessful] Started
2020-01-19 09:47:25.850 46878:407 - INFO:GCActivityDetailViewController.m:764:-[GCActivityDetailViewController selectNewActivity:] <GCActivity cycling:__connectstats__221093 2020-01-17 31:25 10.07 km>
2020-01-19 09:47:25.890 46878:407 - INFO:GCActivity.m:1209:-[GCActivity loadTrackPoints] <GCActivity cycling:__connectstats__221093> Loaded trackpoints count = 1837 [0.0 sec 66.0 Mb (+12.6 Mb)]
2020-01-19 09:47:26.747 46878:3f07 - INFO:GCActivitiesOrganizer.m:383:-[GCActivitiesOrganizer loadFromDb] Loaded 3677 activities [1.1 sec 215.4 Mb (+187.6 Mb)]

You can choose before sending a bug report to include activities. If this is selected in addition the log above, the internal database of activities saved on your phone is sent as well. This contains all the high level data (distance, heart rate, timings, etc) that allows reconstruction of the statistics page. In addition it will include all the details of the currently selected activity in the detail page (only one full detailed activity). These details contains all the gps points.

Because when you send a bug report, I ask for an email address so I can reply to you, in this case that data could be traced back to an individual with that email. But as mentioned before, I am the only one that receive that email or have access to the files where they are saved on my server. This by the way is the same server hosted by GoDaddy where I have host all the data for ConnectStats.

Conclusion

I hope this will relieve any concerns any one could have about privacy of their data in ConnectStats.

Happy to answer any more questions, and of course if anyone finds holes or gap in how I implemented ConnectStats, feel free to reach out either by comment below or via email or GitHub issue.

One thought on “Data Privacy in ConnectStats

  1. I wish I could comment on that AppStore review. Iā€™d definitely highly support you – very happy, loyal long-time user here šŸ˜„
    PS: Seems that on this site the login via WordPress & Google doesn’t work. Currently

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.