# I Wasted 3 Days on Google Drive 'Open With' OAuth — Here's Everything I Learned

> Source: <https://dev.to/hetianhe/i-wasted-3-days-on-google-drive-open-with-oauth-heres-everything-i-learned-1h55>
> Published: 2026-11-12 02:09:42+00:00

Three days. That's how long it took me to fix a single OAuth error that, in hindsight, had a five-minute solution. The catch? The fix was in a place I never thought to look.

If you're building a **Google Drive "Open With" App** and you're seeing:

```
Error 400: redirect_uri_mismatch
```

...this post is for you. I'll walk through exactly how to diagnose it, why the usual advice doesn't work, and the specific steps that actually fixed it.

I was building an AI powered document processing tool [RenomeeAI](https://renomeeai.com/rename-files/pdf/) that integrates with Google Drive. The idea: users right-click a file in Drive → "Open With" → my app → OAuth flow → process the file.

Here's the full flow:

```
┌──────────────┐         ┌─────────────────────┐         ┌──────────────┐
│ Google Drive │         │  drive-api.renomeeai │         │  Google APIs │
│   (client)   │         │     (FastAPI)        │         │              │
└──────┬───────┘         └──────────┬───────────┘         └──────┬───────┘
       │                            │                             │
       │  1. Right-click → "Open    │                             │
       │     With" — Drive builds   │                             │
       │     OAuth URL              │                             │
       │──────────────────────────►│                             │
       │                            │                             │
       │                            │  2. Redirect → Google auth  │
       │◄───────────────────────────┼────────────────────────────│
       │  User clicks Allow         │                             │
       │                            │  3. callback: code → token  │
       │                            │───────────────────────────►│
       │                            │◄───────────────────────────│
       │                            │   access_token+refresh_token│
       │                            │                             │
       │                            │  4. Encrypt → DB, set Cookie│
       │◄───────────────────────────│                             │
       │                            │                             │
       │  5. Frontend: session/init  │                             │
       │──────────────────────────►│                             │
       │                            │  6. Fetch file metadata     │
       │                            │───────────────────────────►│
       │                            │◄───────────────────────────│
       │◄───────────────────────────│   files: [name, mimeType,   │
       │  { needs_auth, files }     │           imageMetadata]    │
       │                            │                             │
       │  7. User enters rename cmd  │                             │
       │──────────────────────────►│                             │
       │                            │  8. Call RenameAI           │
       │                            │──────► api.renameai.com     │
       │                            │◄──────  suggested_name      │
       │◄───────────────────────────│                             │
       │  { suggested_name }        │                             │
       │                            │                             │
       │  9. User confirms → apply  │                             │
       │──────────────────────────►│                             │
       │                            │  10. PATCH Drive filename   │
       │                            │───────────────────────────►│
       │◄───────────────────────────│◄───────────────────────────│
       │  { success, new_name }     │                             │
```

During local development, I used [ngrok](https://ngrok.com/) to expose my local server. I registered my ngrok URL in Google Cloud Console as the OAuth redirect URI and everything worked fine.

When I moved to production, I:

`.env` file
Then I tested the "Open With" flow and got:

```
Error 400: redirect_uri_mismatch
The redirect URI in the request did not match a registered redirect URI.

Request details:
redirect_uri: https://7886-45-78-56-186.ngrok-free.app/drive/open
```

That's my **old ngrok URL**. From months ago. It didn't make any sense.

This is the part that trips up almost everyone, including me.

There are two completely separate OAuth flows in a Drive integration:

| Flow | Who builds the OAuth URL | Where `redirect_uri` comes from | 
|---|---|---|
| Your `/auth/start` route | **Your server** | Your `.env` or config | 
| Drive "Open With" native flow | **Google Drive's servers** | **Drive UI Integration settings** | 

The URL I saw in the Network tab — the one with my old ngrok address — was constructed by **Google's servers**, not mine. My code had nothing to do with it. My `.env` was irrelevant. No matter how many times I restarted my container, the URL would keep using whatever was stored in Google's backend registry for my app.

Once I understood this, everything else clicked.

`redirect_uri` from Chrome DevTools
Before changing anything, get the ground truth.

`accounts.google.com`
The URL you're looking for looks like this:

```
https://accounts.google.com/o/oauth2/auth
  ?scope=https://www.googleapis.com/auth/drive.file
  &client_id=YOUR_CLIENT_ID.apps.googleusercontent.com
  &response_type=code
  &access_type=offline
  &redirect_uri=https://7886-45-78-56-186.ngrok-free.app/drive/open   ← the problem
  &state=%7B%22ids%22%3A%5B%22xxx%22%5D%2C%22action%22%3A%22open%22%2C%22userId%22%3A%22xxx%22%7D
```

Pay attention to the `redirect_uri` parameter. If it's an old ngrok address or any domain you no longer use — this URL was built by **Google's servers**, not yours.

``` python
python3 -c "
import urllib.parse as u
url = 'PASTE_FULL_URL_HERE'
qs = u.parse_qs(u.urlparse(url).query)
print('redirect_uri:', qs.get('redirect_uri', ['not found'])[0])
"
```

If you see an old domain, a ngrok address, or anything that's not your current production URL — that's coming from Google's app registry, not your server. Keep reading.

Google Cloud Console shows you what you *submitted*. The Drive API's `apps.get` endpoint shows you what Drive is *actually using*. These can diverge significantly, especially if your app was configured months ago.

```
   https://www.googleapis.com/auth/drive.apps.readonly
TOKEN="paste_your_access_token"
APP_ID="YOUR_PROJECT_NUMBER"  # Your GCP project number (all digits), NOT project ID

curl -s \
  -H "Authorization: Bearer $TOKEN" \
  "https://www.googleapis.com/drive/v3/apps/$APP_ID?fields=id,name,openUrlTemplate,installed,authorized" \
  | python3 -m json.tool
```

**Note:** `APP_ID` is your **project number** (all digits), not the project ID string. Get it with:

```
gcloud projects describe YOUR_PROJECT_ID --format="value(projectNumber)"
```

**This is where I found the smoking gun.**

The `openUrlTemplate` field looked like this:

```
"openUrlTemplate": "https://accounts.google.com/o/oauth2/auth?scope=...&redirect_uri=https://7886-45-78-56-186.ngrok-free.app/drive/open&..."
```

Google had literally hard-coded my old ngrok URL inside the OAuth template that Drive uses to build authorization requests. This template lives server-side at Google. Changing my GCP Console settings didn't update it.

Navigate to:

```
Google Cloud Console
  → APIs & Services
  → Enabled APIs & Services
  → Google Drive API
  → Drive UI Integration tab
```

Look for this checkbox:

```
☐ Automatically show OAuth 2.0 consent screen when users open my
  application from Google Drive
```

**This was checked on my app.** This is where the wrong `redirect_uri` came from in the first place.

When this option is enabled, Drive doesn't send users to your `Open URL` directly. Instead, it builds an OAuth wrapper URL — using the `redirect_uri` you had configured *at the time you first enabled this option* — and writes that wrapped URL into the Drive App Registry as your app's `openUrlTemplate`. That value doesn't update automatically when you later change your Redirect URI in GCP Console.

Google has [deprecated this option](https://developers.google.com/drive/api/guides/enable-sdk) — but plenty of older tutorials still recommend enabling it. Don't.

**Step one: Uncheck it. Fix your Open URL. Save.**

```
Open URL: https://your-production-domain.com/drive/open
```

⚠️ **Important: unchecking is necessary, but not sufficient.**

These are two separate problems. The checkbox controls whether Drive *continues to generate* OAuth wrapper URLs. The Registry cache controls what URL Drive is *currently using*. Unchecking the box stops the bleeding — it doesn't automatically flush the old `openUrlTemplate` that's already stored in the Registry.

You **must** verify via `apps.get` that the Registry has actually updated. Don't trust the Console page.

After saving, re-run the `apps.get` query from Step 2. Check every 10 minutes.

**What you want to see:**

```
"openUrlTemplate": "https://your-production-domain.com/drive/open?state={ids}&action={action}&userId={userId}"
```

No more OAuth wrapper URL. No more old redirect URIs.

**If it's still showing the old value after 1 hour, stop waiting and go straight to "When Nothing Works" below.** The Registry for an old project may simply never sync. Waiting longer won't change that.

This one bit me a second time. Even after the registry showed the correct `openUrlTemplate`, my OAuth flow was still broken — because I had previously installed the app under my test account with the *old* configuration, and that installation was cached.

**The correct re-install sequence:**

`apps.get`:` installed: false`
If you skip step 4 and re-authorize before the registry updates, you'll re-install the old configuration and be back where you started.

I also needed to add image support to my app. I added the MIME types in Console, but after re-installing, images still didn't show up in the "Open With" menu.

The issue: **existing app installations don't pick up new MIME type configurations**. You have to fully uninstall and reinstall (following the sequence above).

Also, put file types under **Secondary** rather than Default:

```
Secondary MIME Types:
  application/pdf
  image/jpeg
  image/png
  image/webp

Secondary File Extensions:
  pdf, jpg, jpeg, png, webp
```

Setting types as "Default" makes your app the default opener, which is usually not what you want.

If you've done everything above and `apps.get` still returns the old `openUrlTemplate` after an hour, your project's app registry entry may be in a broken state. Google's backend caching for `openUrlTemplate` can sometimes get stuck.

**Option A: Disable and re-enable the Drive API**

```
gcloud services disable drive.googleapis.com --project=YOUR_PROJECT_ID
gcloud services enable drive.googleapis.com --project=YOUR_PROJECT_ID
```

Then re-save your Drive UI Integration settings. Sometimes this forces a registry refresh. Not guaranteed.

**Option B: Create a new GCP project (recommended — and what actually fixed it for me)**

After nearly a full day on the old project, I created a new one. The very first `apps.get` query returned the correct `openUrlTemplate`. The whole "Open With" flow worked on the first attempt. In hindsight, the time I spent waiting for the old project's Registry to sync was entirely wasted — a new project has no historical cache at all.

```
# Create the new project
gcloud projects create my-app-drive-prod --name="My App Drive Prod"

# Enable Drive API
gcloud services enable drive.googleapis.com --project=my-app-drive-prod

# Get the project number for apps.get queries
gcloud projects describe my-app-drive-prod --format="value(projectNumber)"
```

Configure the new project from scratch:

Update your server's `.env`:

```
GOOGLE_DRIVE_CLIENT_ID=new_client_id
GOOGLE_DRIVE_CLIENT_SECRET=new_client_secret
```

Rebuild your container:

```
docker compose up -d --force-recreate app
redirect_uri_mismatch error
         |
         v
F12 → Network → copy OAuth URL → extract redirect_uri
         |
         v
Is redirect_uri wrong (old ngrok, wrong domain)?
         |
         YES
         v
OAuth Playground → get token → GET /drive/v3/apps/{appId}
         |
         v
Does openUrlTemplate contain an OAuth wrapper URL?
         |
         YES
         v
Drive API → Drive UI Integration tab
→ UNCHECK "Automatically show OAuth 2.0 consent screen"
→ Fix Open URL → Save
         |
         v
Check apps.get every 10 min → is openUrlTemplate updated?
    |                              |
    NO (>1 hour)                  YES
    |                              |
    v                              v
Create new GCP project        Uninstall app → wait for apps.get
                              to show installed:false → reinstall
```

`GET /drive/v3/apps/{appId}`
*If this saved you some time, the [Google Drive API docs on UI Integration](https://developers.google.com/drive/api/guides/enable-sdk) are worth a read — especially the deprecation notices on older features.*
