Setting up Firebase and Google Sign-In in my old Flutter app for Web
I've been running an Flutter app for years on Android and iOS. Recently, I wanted to support Web. It should be as simple as running flutter build web, then using Firebase Hosting to publish it to some domain. It ended up being slightly more complicated than that.
Initial set up
First I was quite pleased that the firebase CLI supports multiple logins.
firebase login:add
Visit this URL on this device to log in:
https://accounts.google.com/o/oauth2/auth?...
Waiting for authentication...
✔ Success! Added account [REDACTED]then I added the hosting information.
% firebase init hosting
######## #### ######## ######## ######## ### ###### ########
## ## ## ## ## ## ## ## ## ## ##
###### ## ######## ###### ######## ######### ###### ######
## ## ## ## ## ## ## ## ## ## ##
## #### ## ## ######## ######## ## ## ###### ########
You're about to initialize a Firebase project in this directory:
/[project_path]
=== Account Setup
Which account do you want to use for this project? Choose an account or add a new one now
? Please select an option:
✔ Using account: [REDACTED]
=== Project Setup
First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.
? Please select an option: Use an existing project
? Select a default Firebase project for this directory:
i Using project [PROJECT_NAME]
=== Hosting Setup
Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.
? What do you want to use as your public directory? build/web
? Configure as a single-page app (rewrite all urls to /index.html)? Yes
? Set up automatic builds and deploys with GitHub? No
✔ Wrote build/web/index.html
i Writing configuration info to firebase.json...
i Writing project information to .firebaserc...
✔ Firebase initialization complete!Then I build the app for Web: flutter build web, and finally pushed it to a beta channel. I used this doc as a reference https://firebase.google.com/docs/hosting/test-preview-deploy.
% firebase hosting:channel:deploy beta
✔ hosting:channel: Channel beta has been created on site [PROJECT_NAME].
=== Deploying to '[PROJECT_NAME]'...
i deploying hosting
i hosting[PROJECT_NAME]: beginning deploy...
i hosting[PROJECT_NAME]: found 32 files in build/web
✔ hosting[PROJECT_NAME]: file upload complete
i hosting[PROJECT_NAME]: finalizing version...
✔ hosting[PROJECT_NAME]: version finalized
i hosting[PROJECT_NAME]: releasing new version...
✔ hosting[PROJECT_NAME]: release complete
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/[PROJECT_ID]/overview
Hosting URL: https://[PROJECT_NAME].web.app
✔ hosting:channel: Channel URL ([PROJECT_NAME): https://[PROJECT_NAME]--beta-[some hash].web.app [expires 2025-09-13 12:34:03]Everything worked fine except that when I loaded the page, I got a blank page and this error:
Firebase Web set up
In order to read the error message, I ran the project in VSC in Chrome. And I get:
errors.dart:274 Uncaught (in promise) DartError: Assertion failed: file:///Users/[username]/.pub-cache/hosted/pub.dev/firebase_core_web-3.0.0/lib/src/firebase_core_web.dart:288:11
options != null
"FirebaseOptions cannot be null when creating the default app."
at Object.throw_ [as throw] (errors.dart:274:3)
at Object.assertFailed (errors.dart:44:3)
at firebase_core_web.dart:288:18
at async_patch.dart:623:19
at async_patch.dart:648:23
at async_patch.dart:594:19
at _RootZone.runUnary (zone.dart:1849:54)
at Sure enough, my initialization does not pass FirebaseOptions:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
initializeTimeZones();
await initializeDateFormatting(AppLocale);
await Firebase.initializeApp();
runApp(MyApp());
}
Things must have changed between the time I made the app and the latest version of the library. After following the steps at https://firebase.google.com/docs/flutter/setup?platform=ios#initialize-firebase, I get to generate firebase_options.dart based on my project with all the right keys.
My init code now becomes:
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);Next!
Google Sign-In
Uncaught (in promise) DartError: Assertion failed: file:///Users/[username]/.pub-cache/hosted/pub.dev/google_sign_in_web-0.12.4+4/lib/google_sign_in_web.dart:144:9
appClientId != null
"ClientID not set. Either set it on a <meta name=\"google-signin-client_id\" content=\"CLIENT_ID\" /> tag, or pass clientId when initializing GoogleSignIn"
at Object.throw_ [as throw] (errors.dart:274:3)
at Object.assertFailed (errors.dart:44:3)
at google_sign_in_web.dart:144:20
at async_patch.dart:623:19
at async_patch.dart:648:23
at Object._asyncStartSync (async_patch.dart:542:3)
at Where do I find this ClientID? Firebase Console or Google Cloud?...
A quick search says it's indeed in Google Cloud. So I went into the Cloud Console, opened my project, and on the left-hand side navigation bar, I see APIs and Services. Then in that page, I can see:
Identity Toolkit API: The Google Identity Toolkit API lets you use open standards to verify a user's identity.
That's the one. In that page, there's a tab called Credentials and that's where the client ids are.
In my project's web/index.html, I add the <meta> tag:
<meta name="google-signin-client_id" content="[redacted]" />When I try to sign in, I get the following error in the Console:
client:74 [GSI_LOGGER-OAUTH2_CLIENT]: Checking popup closed.
client:74 [GSI_LOGGER-TOKEN_CLIENT]: Handling response. {"access_token":"[REDACTED]","token_type":"Bearer","expires_in":3599,"scope":"email profile https://www.googleapis.com/auth/userinfo.email openid https://www.googleapis.com/auth/userinfo.profile","authuser":"0","prompt":"none"}
...
[GSI_LOGGER-TOKEN_CLIENT]: Trying to set gapi client token.
client:74 [GSI_LOGGER-TOKEN_CLIENT]: The OAuth token was not passed to gapi.client, since the gapi.client library is not loaded in your page.
browser_client.dart:82 GET https://content-people.googleapis.com/v1/people/me?sources=READ_SOURCE_TYPE_PROFILE&personFields=photos%2Cnames%2CemailAddresses 403 (Forbidden)
(anonymous) @ browser_client.dart:82
(anonymous) @ async_patch.dart:623
(anonymous) @ async_patch.dart:648
(anonymous) @ async_patch.dart:594
runUnary @ zone.dart:1849
handleValue @ future_impl.dart:222
handleValueCallback @ future_impl.dart:948
_propagateToListeners @ future_impl.dart:977
[_completeWithValue] @ future_impl.dart:720
(anonymous) @ future_impl.dart:804
_microtaskLoop @ schedule_microtask.dart:40
_startMicrotaskLoop @ schedule_microtask.dart:49
tear @ operations.dart:118
(anonymous) @ async_patch.dart:188
Promise.then
_scheduleImmediateWithPromise @ async_patch.dart:187
_scheduleImmediate @ async_patch.dart:160
_scheduleAsyncCallback @ schedule_microtask.dart:70
_rootScheduleMicrotask @ zone.dart:1623
scheduleMicrotask$ @ schedule_microtask.dart:150
schedule @ stream_impl.dart:645
[_addPending] @ stream_impl.dart:368
[_sendData] @ broadcast_stream_controller.dart:428
add @ broadcast_stream_controller.dart:257
[_onTokenResponse] @ gis_client.dart:183
tear @ operations.dart:118
_callDartFunctionFast1 @ js_allow_interop_patch.dart:224
ret @ js_allow_interop_patch.dart:84
Vt.i @ client:352
(anonymous) @ client:121Understand this error
errors.dart:274 Uncaught (in promise) DartError: ClientException: {
"error": {
"code": 403,
"message": "People API has not been used in project [project id] before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/people.googleapis.com/overview?project=[project id] then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
"status": "PERMISSION_DENIED",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "SERVICE_DISABLED",
"domain": "googleapis.com",
"metadata": {
"serviceTitle": "People API",
"containerInfo": "[project id]",
"activationUrl": "https://console.developers.google.com/apis/api/people.googleapis.com/overview?project=[project id]",
"service": "people.googleapis.com",
"consumer": "projects/[project id]"
}
},
{
"@type": "type.googleapis.com/google.rpc.LocalizedMessage",
"locale": "en-US",
"message": "People API has not been used in project [project id] before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/people.googleapis.com/overview?project=[project id] then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
},
{
"@type": "type.googleapis.com/google.rpc.Help",
"links": [
{
"description": "Google developers console API activation",
"url": "https://console.developers.google.com/apis/api/people.googleapis.com/overview?project=[project id]"
}
]
}
]
}
}
, uri=https://content-people.googleapis.com/v1/people/me?sources=READ_SOURCE_TYPE_PROFILE&personFields=photos%2Cnames%2CemailAddresses
at Object.throw_ [as throw] (errors.dart:274:3)
at people.dart:146:7
at async_patch.dart:623:19
at async_patch.dart:648:23
at async_patch.dart:594:19Now I'm at the point where it might make sense to refer to the Google Sign-In documentation on pub.dev. I find https://pub.dev/packages/google_sign_in_web#integration.
But it's strange that it asks me to enable the People API, since there was never such an error on Android or iOS. Still, I enable it and try to sign in again. And it works!!