Google Chrome extension, Firefox WebExtension & GeckoView extension for retrieving Google OAuth token for Grindr
  • TypeScript 78.3%
  • JavaScript 17.7%
  • CSS 2.3%
  • Shell 1.4%
  • HTML 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-23 22:57:22 +02:00
.forgejo/workflows Check lint, formatting, tests and the build in CI 2026-09-23 22:57:22 +02:00
.vscode Initial commit 2026-07-01 14:33:24 +02:00
chrome Bump to v1.0.6 2026-09-23 20:27:35 +02:00
contrib Fix README and screenshot 2026-09-11 19:29:41 +02:00
firefox Bump to v1.0.6 2026-09-23 20:27:35 +02:00
geckoview Bump to v1.0.6 2026-09-23 20:27:35 +02:00
icons Add correct icons to each target 2026-07-03 18:58:01 +02:00
shared Add hex fallbacks for oklch colors (closes #4) 2026-09-23 20:27:35 +02:00
test Add scenario tests for the background, content script and token page 2026-09-23 22:56:53 +02:00
.editorconfig Adopt @opengrind/config and lefthook 2026-08-07 00:40:02 +02:00
.git-blame-ignore-revs Ignore the mechanical reformat in blame 2026-08-07 00:40:10 +02:00
.gitignore Adopt @opengrind/config and lefthook 2026-08-07 00:40:02 +02:00
build.sh Check lint, formatting, tests and the build in CI 2026-09-23 22:57:22 +02:00
bun.lock Check lint, formatting, tests and the build in CI 2026-09-23 22:57:22 +02:00
bunfig.toml Adopt @opengrind/config and lefthook 2026-08-07 00:40:02 +02:00
eslint.config.js Add scenario tests for the background, content script and token page 2026-09-23 22:56:53 +02:00
lefthook.yml Adopt @opengrind/config and lefthook 2026-08-07 00:40:02 +02:00
LICENSE Initial commit 2026-07-01 14:33:24 +02:00
package.json Check lint, formatting, tests and the build in CI 2026-09-23 22:57:22 +02:00
prettier.config.js Adopt @opengrind/config and lefthook 2026-08-07 00:40:02 +02:00
README.md Show the token in the sign-in tab instead of an extension page (closes #2) 2026-09-23 20:27:34 +02:00

Grindr Google OAuth WebExtension

Note

If you're an Android user, you might be looking for Open Grind or Open Grind Google OAuth Android app instead.

WebExtension that gets OAuth access token via Google Identity Services (GIS) for Grindr sign-in. Made possible by the fact that GIS refuses to run in embedded app WebViews, and GeckoView forbids the host app from injecting JS into pages.

Screenshot

Install

  • Firefox, Librewolf (desktop & Android):
    • Download from addons.mozilla.org (recommended)
    • or download grindr_google_oauth-x.x.x-firefox.zip from Releases, unarchive, load the unpacked directory via about:debugging (This Firefox → "Load Temporary Add-on")
  • Google Chrome, Chromium & Chromium-based browsers (desktop only):
    • Download from chrome.google.com (recommended)
    • or download grindr_google_oauth-x.x.x-chrome.zip from Releases, unarchive, load the unpacked directory via chrome://extensions (Developer mode → "Load unpacked")
  • GeckoView (for developers embedding this project into their Android app):
    • Download grindr_google_oauth-x.x.x-geckoview.zip from Releases, bundle the built web-ext-artifacts/geckoview/ directory in your app's assets/ and install it as a built-in extension via runtime.webExtensionController.ensureBuiltIn(uri, id) (nativeMessaging and geckoViewAddons are privileged permissions that only work for a built-in extension)

Alternatively, clone the repository yourself and build the extension using ./build.sh [firefox|chrome|geckoview] (requires Bun).

Usage

Firefox, Librewolf (desktop & Android) and Google Chrome, Chromium & Chromium-based browsers (desktop only):

  1. Install the extension
  2. Click the toolbar icon
  3. A new tab opens with a button
  4. Click "Sign in with Google"
  5. Complete the OAuth flow in the Google window that opens
  6. Tap "Copy token" on the page and paste it into the host app

GeckoView:

  1. Install the extension as a built-in (see above) and load https://web.grindr.com/ in a GeckoSession. If the session is private, allow the extension in private browsing.
  2. The content script blanks the page and shows a "Sign in with Google" button. After the user signs in, the token is sent to your app over native messaging.
  3. Register the delegate on the extension:
runtime.webExtensionController
    .ensureBuiltIn(
        "resource://android/assets/grindr-google-oauth/",
        "grindr-google-oauth-webextension@opengrind.org",
    )
    .accept { extension ->
        extension?.setMessageDelegate(delegate, "grindr_google_oauth")
        runtime.webExtensionController.setAllowedInPrivateBrowsing(extension!!, true)
    }

The delegate receives:

Message Meaning
{ "type": "token", "token": "ya29..." } the access token
{ "type": "error", "error": "..." } sign-in failed; the reason is already on screen in the page

Answer every message from onMessage:

  • GeckoResult.fromValue(true) once you have taken the token, or for an error you have noted
  • GeckoResult.fromException(...) to refuse it, which puts the page back on "Try again"
  • Any other reply, including false or null, also counts as taken. The page then stays on "Signing in with Google...", so move the session on (the reference app loads shared/token.html#<token>)
  • Reply with a primitive. A JSONObject reply fails with "Invalid event data for callback" and the page hangs until it times out.
  • Reply within 10 seconds. After that the page shows "The app didn't answer." and re-enables the button.

One delegate is kept per runtime, per extension id and native app name, and the last registration wins. If more than one activity shares the runtime, register again in onResume(), or a finished activity keeps the delegate and tokens go nowhere. GeckoView also queues messages sent while no delegate is registered, so register before loading the page.

A session-level delegate (session.webExtensionController.setMessageDelegate(extension, delegate, name)) receives messages from extension pages in that session. This extension sends none, so you do not need one.

The token is then to be used with the /v8/sessions/thirdparty endpoint.

License

MIT