Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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`).

Expand Down Expand Up @@ -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) {
Expand All @@ -319,12 +329,18 @@ if (kIsWeb) {
redirectUri: 'http://localhost:8082/auth/callback',
);
} else {
client.auth.initializeGoogleSignIn();
client.auth.initializeGoogleSignIn(
serverClientId: '<web_client_id>.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.
Expand Down Expand Up @@ -399,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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=<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

Expand Down Expand Up @@ -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: '<web_client_id>.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)).
Comment thread
developerjamiu marked this conversation as resolved.

**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

Expand All @@ -176,6 +201,18 @@ Every line of the JSON must be indented by at least one level more than `googleC

**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.
Expand Down
Loading