Google OAuth on Firebase App Hosting crosses four configuration boundaries:
- Auth.js reads a server-side variable.
- Firebase App Hosting makes that variable available to the deployed application.
- Secret Manager stores the referenced value and permits the backend to read it.
- Google Auth Platform accepts the exact origin and callback URI used in production.
A failure at any boundary can look like a generic sign-in error. The fastest diagnosis is to trace one variable and one redirect from source to destination instead of changing several systems at once.
First decide who names the variables
Auth.js can infer provider credentials from its default environment-variable convention. For Google, the inferred names are:
AUTH_GOOGLE_ID
AUTH_GOOGLE_SECRET
An application can also use custom names when it passes them explicitly to the provider:
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
});
Both patterns are valid. The broken pattern is defining one pair while the code reads the other.
This monorepo deliberately uses GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET through an explicit provider configuration. It also requires AUTH_SECRET and AUTH_TRUST_HOST=true. Those are repository conventions, not names that every Auth.js project must copy.
Before inspecting Firebase, search the deployed code path and write down the exact names it reads. Include aliases or validation wrappers that may transform them.
Map each variable through apphosting.yaml
Firebase App Hosting supports environment variables with build-time, runtime, or combined availability. It also supports values backed by Secret Manager. The current syntax and precedence rules are documented in Configure and manage your App Hosting backend.
A simplified runtime configuration for the repository convention looks like this:
env:
- variable: AUTH_SECRET
secret: auth-secret
availability:
- RUNTIME
- variable: GOOGLE_CLIENT_ID
secret: google-client-id
availability:
- RUNTIME
- variable: GOOGLE_CLIENT_SECRET
secret: google-client-secret
availability:
- RUNTIME
- variable: AUTH_TRUST_HOST
value: "true"
availability:
- RUNTIME
The secret identifiers are examples. Use the identifiers created for the backend, not these literal names.
For a server-only Auth.js configuration, runtime availability is the important boundary. A variable needed while the framework builds static output may also need build availability. Granting broader availability without a consumer is unnecessary and increases the number of identities that may need secret access.
Firebase console configuration can override values declared in apphosting.yaml. If the file looks correct but the runtime behaves differently, inspect the backend’s effective environment rather than assuming the file is the final value.
Verify Secret Manager access separately
A secret can exist and still be unreadable by the App Hosting backend. Firebase documents that the relevant service account needs Secret Manager access for each context where the secret is used.
For secrets managed with the Firebase CLI, use the App Hosting secret commands instead of manually guessing service-account bindings:
firebase apphosting:secrets:set SECRET_NAME
firebase apphosting:secrets:grantaccess SECRET_NAME --backend BACKEND_ID
Check the current Firebase CLI documentation before automation because command options can change.
Do not print secret values while diagnosing access. A safe probe reports whether a required variable is present and its length category, never the value itself. Remove the probe after diagnosis.
Match the Google callback URI exactly
For a standard Auth.js Google provider, the callback path is:
https://YOUR_PRODUCTION_HOST/api/auth/callback/google
Add that exact URI to the web OAuth client. Scheme, host, path, and any port must match. Google’s OAuth client guidance notes that a mismatch produces redirect_uri_mismatch; it also recommends keeping the client secret in a secure server-side store such as Secret Manager. See Manage OAuth clients.
Also check:
- the OAuth client belongs to the intended Google Cloud project;
- the app’s audience and test-user state permit the account being used;
- the public product name and authorized domains match the production site;
- the requested scopes are declared and no broader than necessary;
- the homepage and privacy-policy URLs are public and accurate where verification requires them.
Do not use the Firebase backend hostname as the callback simply because Firebase runs the application. Use the origin that the browser actually uses for the authentication request.
Use the authentication endpoints as a diagnostic sequence
Work from the application outward:
- Open
/api/auth/providers. Confirm that Google is listed. If it is absent, the provider was probably not configured because a variable is missing or validation rejected it. - Start sign-in and inspect the redirect URL. Confirm that the OAuth client ID is the expected one and the callback points to the production origin.
- Inspect the provider response. A Google
redirect_uri_mismatchis a provider-client configuration problem, not a Firebase routing problem. - Inspect the callback response and server logs. State, cookie, secret, or session failures occur after the provider redirect.
- Confirm the session endpoint works after callback completion.
Change only the boundary implicated by the failing step, then redeploy and repeat the same probe.
Distinguish common failure classes
| Symptom | Most likely boundary | First check |
|---|---|---|
Google missing from /api/auth/providers | Application environment | Exact variable names read by provider configuration |
| Backend cannot start auth route | App Hosting or Secret Manager | Runtime availability and secret access |
redirect_uri_mismatch | Google OAuth client | Exact production callback URI |
| Consent screen blocks account | Google app audience | Publishing status, audience, and test users |
| Callback fails after successful consent | Auth.js runtime | AUTH_SECRET, trusted host, cookies, callback logs |
| Works on backend URL but not custom domain | Origin configuration | Production host, trusted-host setting, callback URI |
This table narrows the search but does not replace the logs. Preserve the first useful error and its request stage before retrying.
Deployment checklist
Before a production rollout:
- confirm the code and
apphosting.yamluse the same variable names; - confirm every server secret is referenced, available at runtime, and readable by the backend;
- set a stable
AUTH_SECRET; - set
AUTH_TRUST_HOST=truefor this repository’s App Hosting deployments; - register the exact custom-domain callback URI in the correct OAuth client;
- verify audience, test users, authorized domains, homepage, and privacy policy;
- test cancellation, a permitted account, and a non-permitted account;
- confirm authentication secrets never enter client bundles, logs, or analytics.
The useful mental model is a chain, not a collection of toggles. Auth.js, App Hosting, Secret Manager, and Google each have one observable responsibility. Locate the first broken link before changing the next one.