• Kotlin 53.3%
  • Nix 43.5%
  • Dockerfile 3.2%
Find a file
2026-07-04 15:44:08 +02:00
app Add programmatic token return intent for authorized callers 2026-07-04 01:38:19 +02:00
contrib Initial commit 2026-07-03 23:13:00 +02:00
extension@93fe9a2987 Initial commit 2026-07-03 23:13:00 +02:00
gradle Initial commit 2026-07-03 23:13:00 +02:00
.dockerignore Initial commit 2026-07-03 23:13:00 +02:00
.envrc Initial commit 2026-07-03 23:13:00 +02:00
.gitignore Initial commit 2026-07-03 23:13:00 +02:00
.gitmodules Initial commit 2026-07-03 23:13:00 +02:00
build.gradle.kts Initial commit 2026-07-03 23:13:00 +02:00
BUILDING.md Initial commit 2026-07-03 23:13:00 +02:00
docker-compose.yml Initial commit 2026-07-03 23:13:00 +02:00
Dockerfile Initial commit 2026-07-03 23:13:00 +02:00
flake.lock Initial commit 2026-07-03 23:13:00 +02:00
flake.nix Initial commit 2026-07-03 23:13:00 +02:00
gradle.properties Initial commit 2026-07-03 23:13:00 +02:00
gradlew Initial commit 2026-07-03 23:13:00 +02:00
gradlew.bat Initial commit 2026-07-03 23:13:00 +02:00
LICENSE Initial commit 2026-07-03 23:13:00 +02:00
README.md Update README.md 2026-07-04 15:44:08 +02:00
settings.gradle.kts Initial commit 2026-07-03 23:13:00 +02:00

Open Grind Google OAuth

An Android companion app that obtains a Google OAuth token for signing in to third-party Grindr clients such as Open Grind.

Why a separate app?

Full discussion: open-grind/open-grind#27

Why not the native Google OAuth flow?

The official Grindr app uses Google Identity Services installed on the Android device to authenticate user. This means the app calls host system's GIS, which checks both caller package id (which must be com.grindrapp.android and not org.opengrind, and setting latter to former would cause conflict errors for users who use both) and, more importantly, package signature (which is not possible to forge, since we don't hold Grindr's JKS; signature spoofing is only possible on some ROMs and rooted devices).

There is no way to launch native Google OAuth flow for Grindr's official app from 3rd party client using the official Google Play Services.

Why not use microG or another free reimplementation?

MicroG itself does not allow spoofing, as it's a drop-in that works via the same APIs. YouTube ReVanced ships its own patched microG. However there's still a problem: native Google OAuth flow requires DroidGuard token, which is obtained by executing a proprietary opaque blob generated by Google web API per-request inside of a proprietary opaque attestation VM requiring permissions to the device to validate it's a real physical device and not an emulator. DroidGuard VMs are completely obfuscated and reverse engineering attempts by microG developers failed. See microG discussion.

There is no way to complete native Google OAuth flow without having a real physical Android device and running proprietary opaque executables from Google. This defeats both cross-platform goal of Open Grind and full transparency.

Can we authenticate user with Google OAuth without native flow?

Yes, that's how web.grindr.com works. The entire authentication is web flow, meaning it's supposed to run in browsers, without requiring any device attestation tokens, so it's possible to run it on any platform, even in emulators. Web flow is launched from web.grindr.com with responseType=postMessage and the resulting code is posted by GIS JavaScript with web.grindr.com origin, so only pages with web.grindr.com origin can receive the callback. Additionally, browsers (even embedded ones) prevent actions such as opening new tabs without user gesture, which is why a tap needed on the launch page.

However, there's another problem: Google OAuth Web Flow page checks aggressively for any signs of rendering the login page in embedded windows (including system WebViews, which is what Tauri uses under the hood of Open Grind UI), and while changing User-Agent and removing Sec- headers is possible for both WebKit-based WebViews (WKWebView for macOS & iOS, WebKitGTK for Linux) and Chromium-based WebViews (WebView2 for Windows, Chromium for Android), Android's Chromium WebView specifically adds a special X-Requested-With header that's impossible to remove. This has been pushed by Google specifically for "fraud/abuse detection" (i.e. to detect Android WebView in their services, such as the OAuth page). In 2023 it was announced Google starts a trial to allow developers to opt-out of sending this header, but in 2025 the decision was reversed and X-Requested-With is now sent on all Android system's Chromium WebView requests with no option to disable it, which is exactly what triggers Google OAuth page "security checks" and rejects attempts to sign in.

All other platforms work just fine by rendering Google's OAuth page directly in platform's native WebView, so it's only an issue on Android.

Can we bundle a different WebView for Android?

There aren't many choices: for Android only Chromium WebView and Mozilla's GeckoView are available. While it's possible to bundle one with the Open Grind's app, there are two practical concerns:

  1. WebView libraries are massive. Chromium is 200+ MB, GeckoView is slightly lighter being ~100 MB.
  2. Many users won't even need to ever sign in with Google, so adding 100MB+ to app size just to render one page isn't practical.
  3. Additionally, publishing an app to F-Droid's official repository requires building every dependency from source, and building GeckoView takes hours even on powerful machines — this is why you don't see any apps with GeckoView in the F-Droid repository (except for browsers such as Fennec and Tor); also GeckoView includes Google Services, so we'd have to patch it before building from source

Each CPU architecture adds ~100MB to the universal bundle. So Open Grind apk grows from ~70MB to ~500MB just for one page used once only by some people.

Can we bundle GeckoView with Android app and render the app itself in it?

No, Tauri's wry only works with system's WebView (i.e Chromium).

How do we solve all these issues?

To solve these Google OAuth issues, we've built the Grindr Google OAuth WebExtension. It's a browser extension (Firefox, any Firefox-based browser such as Librewolf, Firefox for Android, Google Chrome, Chromium, any Chromium-based browser such as Brave) that launches web.grindr.com (to satisfy the launch page's origin requirement), replaces document with a simple "Sign in with Google" button, then intercepts the response token from Google OAuth page before it hits Grindr Web's API and either prints it or passes to GeckoView for programmatic intent.

So why build an app if it's an extension?

The whole app is basically a headless browser powered by GeckoView embedding the extension. The reason is simply because most Android users don't have Firefox and use Google Chrome for Android instead, which does not support extensions.

Also, the app features a programmatic intent, which allows the Open Grind app to call this app and get the token back automatically without copy-pasting. For security, this only works if both apps are signed by the same JKS.

If you're advanced user and prefer to avoid installing unnecessary app, consider using the browser extension directly and copy-pasting the token into Open Grind app.

Download

Building

See BUILDING.md.

Security

Releases are signed with Open Grind governance keys; never install from unofficial sources.

License

MIT