The OAuth token broker, when it helps and when it is cargo cult
With PKCE the mobile client holds no secret. The Cloud Function broker is not mandatory, it is a choice. Here are the good reasons to make it, and the bad ones.
I built a token broker I technically did not need. I kept it anyway, and both halves of that sentence deserve an explanation.

Last installment of my photo series: after the source landscape, the contradictory headers and the Dropbox accents, the architecture question that comes up every single time.
What PKCE changed
The historical argument for the broker: "a mobile client cannot keep the client secret, so you need a server". That was true before PKCE.
With the Authorization Code Flow + PKCE, the client generates an ephemeral code_verifier per authorization session. There is no durable secret to protect in the app. Serious providers (Google, Microsoft, Dropbox) all support this flow for public clients, no client secret involved.
Consequence: a mobile app can run OAuth end to end, store tokens in the Keychain or EncryptedSharedPreferences, and talk to the API directly. It is clean, documented, designed for this.
So the broker is not a security obligation. It is an architecture choice. And like any choice, it has good and bad reasons.
The good reasons for a broker
Refresh tokens off the device. If the broker exchanges the code and keeps refresh tokens server-side, a stolen or compromised device only exposes short-lived access tokens. For sensitive scopes (an entire photo library), that is a real gain.
Central revocation. Deleted user, detected abuse, incident: server-side, you cut all access in one request. Device-side, you hope the app gets launched to receive the order.
Out-of-session work. If your product must access photos while the app is closed (server-side background import, batch processing), tokens have to live server-side. That is no longer a choice, the product need decides.
Secret and scope hygiene. A broker enables secret rotation through Secret Manager without shipping a new app version, and server-side verification that requested scopes stay read-only: nobody can widen permissions from a modified client. Those are the reasons I kept mine.
Architecture bonus, learned by reading a bill: the broker handles tokens, never bytes. Proxying the images themselves through the Cloud Function doubles your egress costs for zero benefit. Binaries travel directly between the device and the photo cloud.
The bad reasons
- "It is more secure" with no identified threat: no. A broker adds an attack surface (the server holds every user's tokens), a point of failure, and GDPR compliance work. Concentrating all refresh tokens in one place is only a gain if everything else follows.
- "Everyone does it this way": the tutorial you are copying was written before PKCE, or for a provider that did not support it.
- "We might need it later": you are adding a Cloud Function today, plus its monitoring, secrets and updates, for a hypothetical need.
My decision grid
Three questions, in order:
- Does the product need data access while the app is closed? Yes → broker, end of discussion.
- Are the scopes sensitive enough to justify off-device refresh tokens, and do you have the ops maturity to protect that server? Yes to both → broker.
- Otherwise → direct PKCE, tokens in the OS secure storage, and one less Cloud Function to maintain.
Honesty about the "not mandatory" part matters: a poorly protected broker is worse than no broker at all.
Takeaways
- PKCE removed the "no secret on mobile" argument: the direct flow is legitimate and designed for.
- A broker is justified by three precise needs: off-device tokens, central revocation, out-of-session access.
- Without one of those, it is cargo cult that costs ops and concentrates risk.
Does your current broker answer an identified need, or a 2019 tutorial?