Receiving Facebook Messenger Notifications Without Compromising Your Privacy
I’m not a huge fan of facebook. I had an account as a youth, deleted it in college, and then later realized that facebook marketplace was the new craigslist, and that I’d miss out on buying and selling local goods if I limited myself to the marketplace of web 1.0. Oh well, account reinstated (for marketplace purposes only).
In order to sell items on marketplace, you need to use facebook messenger. Now, I have no issue with getting an email or something when I have a new message in my inbox, and then logging into facebook (in a web browser, in a container tab, on a VPN). I’ve yet to come across a 3rd party client that has all of marketplace’s functionality, so for now I’m stuck using the desktop website. However, in order to drive engagement and distribute malware, facebook doesn’t give you the option of sending external notifications for incoming messages. In order to get an alert that someone contacted you through messenger, facebook requires you to either be on the website at the time or install any of their desktop or mobile applications. Needless to say, I’m not going to do that.
Ok, there has to be at least one (official or otherwise) functional API client that I can use for this, right? Not quite. I’d been down this path before, making a simple auto-reply bot to instruct my friends to reach out via email instead, before my earlier departure from the platform. However, API endpoints change and the library I used (fbchat) is no longer actively maintained. That said, I don’t mean to knock it - it just needs constant poking, since facebook still loves to break things.
Continuing my search for unofficial API clients, I came across Unzuckify. The program was quick to try out, and at first glance, it fit all of my criteria. I forked it for reasons. You can find my fork here. However, after messing with it for a while, I found that it didn’t catch message requests or marketplace messages. That said, if you’re just looking to forward messages to/from friends, I recommend checking it out. My fork logs messages to a standard logging namespace, so the messages can effectively be sent anywhere with a small amount of code changes.
So, why are message requests and marketplace messages different than regular messages, and how can I get notifications for them? Let’s talk about facebook messenger’s APIs.
Unzuckify uses messenger’s GraphQL API to perform actions, such as listing the inbox contents or interacting with a thread. You can read Radon Rosborough’s blog post on reversing this API here. fbchat, on the other hand, uses messenger’s MQTT API and the GraphQL API, depending on the function. Message requests and marketplace messages are shown exclusively through the MQTT API. As mentioned previously, fbchat is currently unmaintained, and by default, has a bit of trouble authenticating to messenger. You can get it to work if you poke at the library enough, though we’ll see that fbchat didn’t solve my problems, either…
Once I got fbchat working [1], the next step was to chase down what MQTT topics spat out message requests and marketplace messages. For that, I spent quite some time in firefox’s dev console, tracing WS-MQTT calls until my eyes bled. After dropping and picking up the project a few times without progress, I realized that even if I did get a client to work, I’d likely need to fix it every so often, as new features are added and old APIs are deprecated. Who knows, in a year from now, the entire project could be for naught!
With this new attitude, I went back to the drawing board and thought about how messenger notifications (or data) can be delivered by 1st party applications.
- Web application (desktop or mobile site)
- Android/iOS - Messenger/Messenger Lite in-app (FB APIs)
- Android/iOS - Push notifications (Firebase Cloud Messaging)
- Windows/MacOS - Messenger Desktop in-app
- Windows/MacOS - Messenger Desktop system notifications
- …
- KaiOS - Facebook app?
Just kidding with KaiOS - according to this reddit thread, the facebook application doesn’t even support sending notifications for messages!
If we want to get notifications as a user would - without directly interfacing with facebook’s APIs - we have a few options. The simplest and most disgusting option would be to load the desktop app in a selenium tab and read the tab’s title (or perhaps set it to log when sounds are made by the page). It would be error-prone and wouldn’t get message content, but it would at least give the user a ping to check facebook. At the end of the day, that’s my minimum viable product. But rather than taking the gross and easy path, I decided to investigate Firebase Cloud Messaging.
Firebase Cloud Messaging
If you’re unfamiliar with how FCM (formerly called Google Cloud Messaging) works, you’re not alone. At a very high level, FCM provides a way to have a client device poll a single server for push notifications from multiple services, so that the device doesn’t have to run each notification-aware process at the same time, thereby conserving device resources. It’s pretty cool, except for the fact that Google’s involved. For a similar FOSS application/system, see UnifiedPush.
For our purposes, we’ll want to do the following:
- Install messenger on a rooted Android device w/ microG
- Log into messenger and make sure it registers on FCM
- Extract the FCM credentials from the android device
- Format or otherwise forget about the android device, without de-registering messenger from the FCM account
In this approach, we only need to run messenger when we’re getting the system set up. From that point onwards, we should get notifications (that may be devoid of information) for all incoming messages.
With a path forward lain out, I went to researching FCM. There’s a lot to read. And worse, most people are only interested in connecting to FCM from the application server side of things, or making sure they’re using the right proprietary libraries for Android’s FCM client to make their mobile apps work. In my case, I’ll need my own FCM client, and I’ll need to rip whatever identifiers messenger uses for FCM authentication out of a pre-existing installation. After far too much time, I found go-fcm-receiver. The readme defined which client identifiers were required to subscribe to a given FCM sender, so the next step was to fetch the identifiers from an authenticated FCM client. I’ll use Android, since I have experience with it.
When initializing an FCM client (for a single sender), you need the following parameters (taken from the above project’s readme):
SenderId: // Firebase Project ID
GcmToken: "<GCM_TOKEN>",
FcmToken: "<FCM_TOKEN>",
AndroidId: 5240887932061714513, // The androidId returned when the device was created
SecurityToken: 69534515778185919, // The securityToken returned when the device was created
Additionally, you’ll need two keys - PRIVATE_KEY_BASE64
and AUTH_SECRET_BASE64
.
I spent a large amount of time poking around in another FOSS FCM client library - microG. A tl;dr for microG is that it’s a FOSS implementation of Google’s proprietary Android library, Google Play Services. Among many services, microG contains an FCM client.
Here’s what my hunt for those values revealed:
SenderId: Firebase Project ID - A 12 digit string taken from an unpacked APK's /res/values/strings.xml file
GcmToken: ???
FcmToken: _presumably_ taken from /data/data/<APP>/files/PersistedInstallation.json -> AuthToken
AndroidId: The androidId returned when the device was created - taken from /data/data/com.google.android.gms/shared_prefs/checkin.xml -> androidId
SecurityToken: The securityToken returned when the device was created - taken from /data/data/com.google.android.gms/shared_prefs/checkin.xml -> securityToken
The next step would be to find where PRIVATE_KEY_BASE64
and AUTH_SECRET_BASE64
are sourced, but this too was stopped due to burnout. I still hope to finish the FCM client method one day, but today is not that day. For now, I just want notifications for facebook messages without having an application installed on my phone.
But what if I have messenger running on something that isn’t my phone? Keeping in the vein of getting notifications “as a user would”, I can set up an Android device (or VM) with microG and a notification forwarding application, install messenger on it, stuff it in a closet connected to a wall outlet, and leave it to forward notifications. Ok, there’s a bit more to my implementation than that.
Android Notification Forwarding
First, I’ll be using the same device I mentioned in my SMS Forwarding post. To reiterate, it’s a Nexus 5 running LineageOS 16 (Android 9) with a VNC server that starts on boot. Searching for notification forwarding apps, I quickly came across Notifikator. With the last release being from 2019, I was skeptical that it’d work on modern versions of Android. And yet, it ran fine on my phone running Android 12. I also had no issue on the Nexus 5. A few adjustments are required for this app to forward notifications how I’d like - first, it doesn’t allow a “free-form” format for sending JSONs, so if you want to log messages to a service such as Gotify, you’ll need to either send Notifikator’s JSON to another service that’ll forward the message to your notification server for you, or implement “gotify” as a protocol in Notifikator. I chose the latter option. My fork is available here, but at this time I wouldn’t recommend using it. I have plans to implement a package deny list (asked for in this issue), but for now I’ve been alright with simply hard-coding the deny-list in code. I still have a lot to learn in mobile app development.
Last, there’s a highly recommended hardware modification to be made. With this phone being connected to power and locked in a closet for eternity, the battery will eventually swell. To quell that, we can simply connect the main board to 4.2V at all times and remove the potential fire hazard. I used this adapter to step from 5V to 4.2V, and affixed it and a USB-C port to the back of the device. From there, it just needs 5V from a normal phone charger. For bonus points, I connected it to an uninterruptible power supply. newscrewdriver.com has a particularly good write-up on this process - using the same phone, no less.
Closing
The word “burnout” appears all too frequently in this post. As is probably obvious, I’ve written this post chunks at a time, stretched across several months. I’m not proud of it, or of my accepted solution. However, in the words of The Zen of Python, “Now is better than never.” Having a FOSS FCM client to forward notifications to a notification server would obviously have further-reaching implications than just those of facebook notifications, though at this time I don’t have a need for it. I’m also not keen on having yet another piece of physical infrastructure to manage, or the implications of (purposely) running an outdated version of Android that’s missing recent security patches. Nevertheless, it is a valid solution to my problem.
[1] For those that want to know just how to poke fbchat to get it to work (as of writing), I have the following notes from several months ago when I was working with it.
- broken login - fixed by allowing redirects after login and forcing the
revision
parameter to 1. - horrid user-agent - just pick a better one