"Mobile Ordering" is a feature native to the iOS and Android Starbucks app. Let's see how hard it would be to do this from an AWS IoT Button.
Starbucks doesn't have any public API documentation, so I needed to reverse engineer the app myself.
Step 1: Bypass Certificate Pinning
I now have a rooted Android emulator that forces all apps to accept any SSL certificate. Moving right along...
Step 2: Monitoring App Traffic
It turns out the latest Starbucks app requires a newer version of Android than the emulator supports.
So I headed over to APK Mirror to download an older version of the Starbucks app.
Unfortunately, the only version of the app that can be installed on the emulator is no longer supported by Starbucks. Using my HTTPS Proxy of choice, Charles, I checked out what was going wrong.
The app seemed to be reporting its version number and receiving instructions to show the "Update Required" screen. To stop this from happening, I added a Charles Rewrite Rule to change "4.0" to "7.0."
The app then let me continue and use the Mobile Order feature.
Finally, I could start monitoring API calls.
Step 3: Spoofing API Calls
Since I had visibility into the app's behavior, I could start recreating the API calls myself in Python. After digging around some, I discovered Starbucks's backend is hosted by the API Management company, Mashery, and their mobile app uses OAuth for authentication with signed token requests. These signatures seemed to expire after a few minutes.
In order to generate my own signatures, I needed to extract the cryptographic keys that are hardcoded in the app.
Hashing a concatenation of the client_id, client_secret, and a UTC epoch timestamp allowed me to create my own signatures. I could then use my Starbucks credentials to obtain a Bearer Token, which is required for authenticating subsequent calls.
Step 4: Writing a Library
Next, I wrote a Python library to take care of spoofing API calls for me.
This library can be used to interface with any Starbucks account. (The credentials below are fictitious)
Placing an order requires a store_id, card_id, and a drink JSON file.
Step 5: Making an AWS Lambda Function
Since I could finally programmatically place Mobile Orders, it was time to upload my code to the cloud.
I used environment variables to dynamically insert my credentials. This will let me change the configuration without uploading new code. I also used Amazon's SNS service to send my phone a text message confirming the order.
Lastly, I hooked up an AWS IoT Button to my new Lambda function. That's it!