From 4b233f152ef4ef9a8294c532659e9a279049a205 Mon Sep 17 00:00:00 2001 From: developerjamiu Date: Thu, 30 Jul 2026 11:27:24 +0100 Subject: [PATCH 1/2] docs: Clarify Google sign-in setup for web and Android --- .../04-providers/03-google/01-setup.md | 34 ++++++++++++----- .../03-google/04-troubleshooting.md | 37 ++++++++++++++++--- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/docs/06-concepts/04-authentication/04-providers/03-google/01-setup.md b/docs/06-concepts/04-authentication/04-providers/03-google/01-setup.md index efaa187a..63461689 100644 --- a/docs/06-concepts/04-authentication/04-providers/03-google/01-setup.md +++ b/docs/06-concepts/04-authentication/04-providers/03-google/01-setup.md @@ -233,26 +233,36 @@ Without the URL scheme, the OAuth callback never returns to your app and sign-in ./gradlew signingReport ``` -4. Click **Create** and download the JSON file. +4. Click **Create**. This registers your app's package name and signing key with Google. ![Create Android OAuth client](/img/authentication/providers/google/9-android-client-create.png) -5. Place the file inside your Flutter project's `android/app/` directory (e.g., `my_project_flutter/android/app/`) and rename it to `google-services.json`. +5. On Android, the sign-in SDK also needs to know your server's client ID. Pass the [Web application OAuth client](#create-the-server-oauth-client-web-application)'s ID as `serverClientId` when you initialize the client (covered in [Initialize the Google sign-in service](#initialize-the-google-sign-in-service) below). You can also pass it at build time with `--dart-define`; see [Configuring Client IDs on the App](./customizations#configuring-client-ids-on-the-app). + +:::note +If your app uses Firebase (the `com.google.gms.google-services` Gradle plugin), you can skip step 5: the plugin supplies the server client ID from `google-services.json`. Re-download that file after creating the Web application client so it includes the web client entry. +::: :::warning -The downloaded `google-services.json` may not include a web OAuth client entry, which is required for Google Sign-In to resolve the server client ID. If sign-in fails, provide the client IDs programmatically as described on the [customizations](./customizations#configuring-client-ids-on-the-app) page. +When testing against a local server, the Android emulator cannot reach `localhost`: sign-in completes, but the endpoint call fails with a connection error. See [troubleshooting](./troubleshooting#endpoint-calls-fail-on-android-with-connection-refused) for pointing the app at your machine. ::: ### Web -On web, Google completes sign-in by redirecting the browser to a callback URL you control. This flow requires Serverpod to serve your Flutter web app on the **same origin** as the callback route. To test locally, build your Flutter web app into Serverpod's `web/app/` directory: +On web, Google completes sign-in by redirecting the browser to a callback URL you control. This flow requires Serverpod to serve your Flutter web app on the **same origin** (same scheme, host, and port) as the callback route. + +:::warning +The web flow only works from the **built** app served by Serverpod (`http://localhost:8082/app` locally). Running the app with `flutter run -d chrome` fails, because Flutter's dev server is a different origin than Serverpod and the browser blocks the sign-in callback; see [troubleshooting](./troubleshooting#sign-in-callback-fails-locally-with-flutter-run--d-chrome). For a hot-reload workflow, use the [separately-hosted Flutter web](./customizations#separately-hosted-flutter-web) flow instead. +::: + +To test locally, build your Flutter web app into Serverpod's `web/app/` directory and start the server: ```bash -flutter build web --output ../my_project_server/web/app # from your Flutter project -serverpod start # from your server project +flutter build web --base-href /app/ --output ../my_project_server/web/app # from your Flutter project +serverpod start # from your server project ``` -Open `http://localhost:8082/app` to test. `flutter run -d chrome` won't work here because Flutter's dev server runs on a different port from Serverpod: for hot-reload workflows, use the [separately-hosted Flutter web](./customizations#separately-hosted-flutter-web) flow instead. +Replace `my_project_server` with your server package directory. Open `http://localhost:8082/app` to test. The examples below use port `8082` (Serverpod's default from `config/development.yaml`). @@ -310,7 +320,7 @@ client.auth.initialize(); client.auth.initializeGoogleSignIn(); ``` -**On web**, the call needs `clientId` and `redirectUri` (matching the route from [Web](#web)). On mobile, leave them unset so the SDK reads its config from `Info.plist` / `google-services.json`: +**On web**, the call needs `clientId` and `redirectUri` (matching the route from [Web](#web)). **On Android**, it needs `serverClientId` from [Android](#android). On iOS, the SDK reads its config from `Info.plist`, and passing `serverClientId` is fine since it holds the same value as `GIDServerClientID`: ```dart if (kIsWeb) { @@ -319,12 +329,18 @@ if (kIsWeb) { redirectUri: 'http://localhost:8082/auth/callback', ); } else { - client.auth.initializeGoogleSignIn(); + client.auth.initializeGoogleSignIn( + serverClientId: '.apps.googleusercontent.com', + ); } ``` Swap the redirect URI for your production URL when deploying. See [Configuring the Web redirect URI](./customizations#configuring-the-web-redirect-uri) to avoid hard-coding it per environment. +:::warning +On web, the app served at `/app` is the build you created in [Web setup](#web). After changing `main.dart` (for example the `redirectUri`), run the build command again and hard-reload the browser. A stale build keeps sending the old values, and sign-in fails with [redirect_uri_mismatch](./troubleshooting#sign-in-fails-with-redirect_uri_mismatch). +::: + ### Show the Google sign-in button The Serverpod template ships with a `SignInScreen` widget at `lib/screens/sign_in_screen.dart`. It listens to `client.auth.authInfoListenable` and swaps between `SignInWidget` while the user is signed out and the `child` you pass it once they sign in. `SignInWidget` auto-detects which identity provider endpoints are registered on the server, so once `GoogleIdpEndpoint` is exposed and the client code has been regenerated, the Google button appears inside it. diff --git a/docs/06-concepts/04-authentication/04-providers/03-google/04-troubleshooting.md b/docs/06-concepts/04-authentication/04-providers/03-google/04-troubleshooting.md index 50be5f5a..22680455 100644 --- a/docs/06-concepts/04-authentication/04-providers/03-google/04-troubleshooting.md +++ b/docs/06-concepts/04-authentication/04-providers/03-google/04-troubleshooting.md @@ -30,10 +30,10 @@ Go through this before investigating a specific error. Most problems come from a #### Client -- [ ] Add `client.auth.initializeGoogleSignIn()` after `client.auth.initialize()` in your Flutter app's `main.dart`. On web, pass `clientId` and `redirectUri` (the full callback URL, either the route URL or the `auth.html` URL, depending on your [Web setup](./setup#web)). +- [ ] Add `client.auth.initializeGoogleSignIn()` after `client.auth.initialize()` in your Flutter app's `main.dart`. On web, pass `clientId` and `redirectUri` (the full callback URL, either the route URL or the `auth.html` URL, depending on your [Web setup](./setup#web)). On Android, pass `serverClientId` (the Web client's ID) unless your app uses the Firebase Gradle plugin. - [ ] Surface Google sign-in in the UI with `SignInWidget` or `GoogleSignInWidget` (see [Present the authentication UI](./setup#present-the-authentication-ui)). - [ ] Create an **iOS** OAuth client in the **same** Google Cloud project as the Web client, using the same **Bundle ID** as the app; set `GIDClientID` from the iOS client, `GIDServerClientID` to the **Web** client's ID, and add the reversed-client-ID **URL scheme** in `Info.plist` (*iOS only*). -- [ ] Create an **Android** OAuth client in the **same** project, with the same **package name** and **SHA-1** as the build you run; place `google-services.json` in `android/app/` (*Android only*). +- [ ] Create an **Android** OAuth client in the **same** project, with the same **package name** and **SHA-1** as the build you run (*Android only*). - [ ] Set up the web callback (*Web only*). Pick one: - **Standard:** Register `FlutterWebAuth2CallbackRoute` on `pod.webServer` in `server.dart` before `pod.start()` per [Web setup](./setup#web). - **Separately-hosted fallback:** Create `web/auth.html` in your Flutter project as described in [Web callback page (`auth.html`)](../../setup#web-callback-page-authhtml) and run Flutter on a **fixed** `--web-port` so the origin does not change every run. See [separately-hosted Flutter web](./customizations#separately-hosted-flutter-web). @@ -59,6 +59,7 @@ Common mistakes: - Trailing slashes, port differences, or `http` vs `https`. - Forgetting the callback path on the redirect URI; the bare origin is not enough. - For separately-hosted Flutter web, the Flutter dev server running on a random port. Pass `--web-port=` to `flutter run` so the origin is stable. +- A stale build on the standard [Web setup](./setup#web) flow. The app served at `/app` is a compiled snapshot, so a `redirectUri` change in `main.dart` takes effect only after re-running `flutter build web`. Rebuild and hard-reload the browser; the service worker can cache the old bundle. ## Production redirect URIs rejected by Google @@ -152,13 +153,37 @@ Every line of the JSON must be indented by at least one level more than `googleC **Resolution:** Register the SHA-1 fingerprint from your release keystore as an additional fingerprint in the Google Auth Platform. You can add multiple SHA-1 fingerprints to the same Android OAuth client, or create separate clients for debug and release. -## Missing web client entry in google-services.json +## Sign-in fails on Android with "serverClientId must be provided" -**Problem:** Sign-in fails on Android with an error about a missing server client ID, or `serverClientId` is null. +**Problem:** Sign-in fails on Android with `GoogleSignInException(code GoogleSignInExceptionCode.clientConfigurationError, serverClientId must be provided on Android, null)`. -**Cause:** The `google-services.json` file does not contain a web OAuth client entry. This happens when no Web application OAuth client exists in the same Google Cloud project. +**Cause:** On Android, the `google_sign_in` SDK requires the server (Web application) client ID, and nothing supplies it. A plain Flutter project does not read `google-services.json`: that file is consumed by the `com.google.gms.google-services` Gradle plugin, which only Firebase-based projects apply. -**Resolution:** Make sure you have created a Web application OAuth client in the same project as your Android OAuth client. Re-download `google-services.json` after creating the Web client. Alternatively, provide client IDs programmatically as described on the [customizations page](./customizations#configuring-client-ids-on-the-app). +**Resolution:** Pass the Web application client ID when initializing, as shown in [Initialize the Google sign-in service](./setup#initialize-the-google-sign-in-service): + +```dart +client.auth.initializeGoogleSignIn( + serverClientId: '.apps.googleusercontent.com', +); +``` + +You can also supply it at build time with `--dart-define=GOOGLE_SERVER_CLIENT_ID=...`; see [Configuring Client IDs on the App](./customizations#configuring-client-ids-on-the-app). + +For Firebase-based projects using the Gradle plugin, make sure a Web application OAuth client exists in the same Google Cloud project and re-download `google-services.json` so it includes the web client entry. + +## Endpoint calls fail on Android with connection refused + +**Problem:** Sign-in completes at Google, but the app then fails with `ServerpodClientException: ... Connection refused ... uri=http://localhost:8080/...`. + +**Cause:** On Android, `localhost` is the emulator or device itself, not the machine running your server. The project template's `assets/config.json` sets `apiUrl` to `http://localhost:8080`, and that value takes precedence over the framework's platform-aware default (see [server URL resolution](../../../endpoints-and-apis)). + +**Resolution:** Point the app at your server explicitly when running on Android: + +```bash +flutter run --dart-define=SERVER_URL=http://10.0.2.2:8080/ +``` + +On the Android emulator, `10.0.2.2` maps to the host machine. On a physical device, use your computer's LAN IP address instead (e.g., `http://192.168.1.20:8080/`), with the phone on the same network. ## People API not enabled From de4a260665b2f1a76eb4383cd0508655a44f58ab Mon Sep 17 00:00:00 2001 From: developerjamiu Date: Thu, 30 Jul 2026 11:48:10 +0100 Subject: [PATCH 2/2] docs: Note hot restart after wiring Google sign-in UI --- .../04-providers/03-google/01-setup.md | 4 ++++ .../04-providers/03-google/04-troubleshooting.md | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/docs/06-concepts/04-authentication/04-providers/03-google/01-setup.md b/docs/06-concepts/04-authentication/04-providers/03-google/01-setup.md index 63461689..d836069c 100644 --- a/docs/06-concepts/04-authentication/04-providers/03-google/01-setup.md +++ b/docs/06-concepts/04-authentication/04-providers/03-google/01-setup.md @@ -415,6 +415,10 @@ body: SignInScreen( ), ``` +:::warning +The `initializeGoogleSignIn` call lives in `main()`, and hot reload does not re-run `main()`. After making these changes, hot restart the app: press **R** in the `serverpod start` terminal, or rerun `flutter run`. Until then, the Google button stays hidden. +::: + The `SignInWidget` renders the standard Google sign-in button: ![Google sign-in button](/img/authentication/providers/google/3-button.png) diff --git a/docs/06-concepts/04-authentication/04-providers/03-google/04-troubleshooting.md b/docs/06-concepts/04-authentication/04-providers/03-google/04-troubleshooting.md index 22680455..67986bcd 100644 --- a/docs/06-concepts/04-authentication/04-providers/03-google/04-troubleshooting.md +++ b/docs/06-concepts/04-authentication/04-providers/03-google/04-troubleshooting.md @@ -201,6 +201,18 @@ On the Android emulator, `10.0.2.2` maps to the host machine. On a physical devi **Resolution:** In the running `serverpod start` terminal, press **M** to create the migration, then **A** to apply it. +## Google sign-in button does not appear + +**Problem:** `SignInWidget` renders, but the Google button is missing. + +**Cause:** `SignInWidget` shows the Google button when the client has a registered `GoogleIdpEndpoint` and the Google sign-in service is initialized. The common misses: + +- The app was hot reloaded after adding `initializeGoogleSignIn` to `main.dart`. Hot reload does not re-run `main()`, so the service is never initialized. +- `GoogleIdpEndpoint` is missing on the server, or the client was not regenerated after adding it. +- On web, `initializeGoogleSignIn` was called without `clientId` and `redirectUri`. The widget renders nothing without them. + +**Resolution:** Hot restart the app: press **R** in the `serverpod start` terminal, or rerun `flutter run`. If the button is still missing, confirm `GoogleIdpEndpoint` exists on the server and run `serverpod generate`, and on web confirm `initializeGoogleSignIn` receives `clientId` and `redirectUri` per [Web setup](./setup#web). + ## Lightweight sign-in (One Tap) not appearing **Problem:** You enabled `attemptLightweightSignIn: true` but the One Tap prompt never appears on Web, or the silent sign-in doesn't trigger on mobile.