Firebase Admin SDK won't run on Cloudflare Workers, so I replaced it with fetch + WebCrypto

2026年8月29日3 次浏览来源:Dev.to阅读原文

Originally published on August 27,

2026.

Republished after an accidental deletion — the content is unchanged.

TL;DR — cannot run on Cloudflare Workers (protobufjs generates code at runtime).

Everything I actually needed is plain HTTP: verify ID tokens with , mint a service-account access token with WebCrypto RS256, use Firestore REST v1 ( / / ) for real transactions, and Identity Toolkit REST to delete users.

About 300 lines, running in production, source on GitHub.

I'm 17, I study at a technical college on a small island in Japan's Seto Inland Sea, and I run a paid membership video platform in production: Okugawa Lab.

It's a Next.js 16 app deployed to Cloudflare Workers through OpenNext, with Firebase Authentication and Cloud Firestore behind it.

Last week I open-sourced the whole thing (toma-okugawa/okugawa-lab).

This post is about the single biggest wall I hit while building it, and the ~300 lines that got me over it.

The wall does not run on Cloudflare Workers.

It's not a configuration problem.

Somewhere under the SDK sits , which generates code from strings at runtime, and workerd forbids that outright: There are no flags to flip.

If you need server-side Firebase on Workers, you don't get the Admin SDK.

What I actually needed was smaller than the SDK anyway: Verify Firebase ID tokens sent by the browser Read and write Firestore, with real transactions (I sell access as serial codes, and redeeming one must be atomic) Delete a user's Auth account when they delete their own data Each of those is an HTTP API.

So that's what I used.

1.

Verifying ID tokens For token verification there's already a solid library: .

It uses only Web-standard APIs and has zero dependencies.

It wants a to cache Google's public keys; the docs use Workers KV, but an in-memory store scoped to the isolate is enough for a small site: One gotcha that cost me an afternoon: the library converts its into a before throwing, and a failure to fetch Google's public keys comes out through the same generic branch.

So you can't tell "your token is bad" (401) from "Google is unreachable" (503) by error class.

I check the message prefix and map that to a 503, and everything else to a

401.

Not elegant, but it keeps a valid session from being logged out because of a transient outage.

2.

A service-account access token with WebCrypto Everything else needs an OAuth2 access token for the service account.

The Admin SDK does this for you; without it, you sign a JWT yourself and exchange it.

Workers has , which is all you need: In the real code the token is cached per scope until 60 seconds before expiry.

I deliberately keep two tokens: one with the Firestore scope () and one with the Identity Toolkit scope.

The code path that reads and writes documents never holds a token that could delete accounts.

Least privilege costs nothing here.

3.

Firestore over REST, with transactions that are actually transactions The part I expected to be painful turned out to be the best part.

Firestore's REST v1 API exposes , , , and directly.

A transaction looks like this: Things worth knowing that the docs don't shout about: can abort too.

Firestore transactions use pessimistic locking on the server side, so a lock conflict can surface at the read step, not only at .

Wrap the whole thing in the retry loop, not just the commit.

ABORTED arrives as HTTP

409.

I classify errors by the gRPC status name in the response body (, , , ) plus HTTP 409/429/500/503 as retryable, which mirrors what the official transaction runner retries.

Deleting a field, not nulling it.

Put the field path in and leave it out of .

That removes it.

Writing keeps the field around with a null in it, which is not the same thing when a privacy law asks you to erase personal data. is a streaming method.

Over HTTP/JSON you get an array of , and some elements carry no at all (progress/end markers).

Filter them out or you'll have a very confusing .

Writes without a transaction are still atomic.

A single applies its writes atomically and in order.

You only need when a write depends on something you read.

4.

Deleting an Auth user Same pattern, different API.

Identity Toolkit REST: I treat as success so the whole deletion flow is idempotent: if anything fails halfway, the user just presses the button again.

And I delete Firestore data before the Auth account, never after; if you delete Auth first and Firestore fails, the person can no longer log in to retry.

Two traps outside Firebase These bit me on the same project and belong in the same post: OpenNext bakes into your Worker bundle. writes every env value into at build time, so a naive deploy uploads your service-account private key in plaintext, even if the runtime value comes from .

I run a small script between build and deploy that strips non-public values and then greps the bundle for the removed strings, failing the deploy if any survive. () Never log the raw error object.

A 's message includes the response body, and the response body includes document names, which in my case are serial codes and user ids.

On a route that erases personal data, that would copy the data you just deleted into Cloudflare's logs.

Log the error class and stage; nothing else.

What this costs Nothing beyond Firebase itself.

No KV namespace, no Durable Objects, no extra packages except the token verifier.

Token exchange happens once an hour per isolate; everything else is one per operation.

The full implementation is in the repository, and the serial-code redemption route that uses the transaction is .

The repo is AGPL-3.0, runs with zero configuration in a preview mode, and I'm happy to take issues and PRs in English or Japanese.

If you've solved the same problem differently, especially the token-cache question across isolates, I'd genuinely like to hear it.

I'm Toma Okugawa, a student researcher at NIT Yuge College in Japan.

Besides this platform I work on pose estimation and low-light image enhancement; the rest of what I do is on GitHub and t-okugawa.dev.

分享
Baike.dev

baike.dev helps you discover great languages, frameworks, databases, DevOps and cloud-native tools.

Quick links

About

Contribute

Found a great developer tool? Share it with the community.

Submit a tool
© 2026 baike.dev Developer EncyclopediaUpdated daily · Discover great developer tools