Skip to content
Open
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
154 changes: 120 additions & 34 deletions docs/06-concepts/11-authentication/04-providers/03-google/01-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,36 @@ The People API is required for Serverpod to access basic user profile data durin

All platforms (iOS, Android, and Web) require a **Web application** OAuth client for the server. This is the only client type that provides a **client secret**, which Serverpod needs to verify sign-in tokens on the server side.

This same Web application client is also used by the web sign-in flow.

On web, Serverpod supports two sign-in modes:

- The default mode uses the Google-hosted web sign-in button from `google_sign_in_web`.
- An optional redirect mode uses OAuth2 Authorization Code + PKCE with an `auth.html` callback page.

1. In the Google Auth Platform, navigate to **Clients** and click **Create Client**.

2. Select **Web application** as the application type.

3. Add the following URIs:

- **Authorized JavaScript origins**: The origin that is allowed to make requests to Google's OAuth servers. For Serverpod, this is your **web server** address.
- **Authorized redirect URIs**: The URL Google redirects the user back to after they sign in. Serverpod handles this callback on the web server as well.
- **Authorized JavaScript origins**: The browser origins that are allowed to start the sign-in flow.
- **Authorized redirect URIs**: The URLs Google redirects back to after sign-in.

Serverpod runs three servers locally (see `config/development.yaml`): the API server on port 8080, the Insights server on 8081, and the **web server on port 8082**. The Google OAuth flow uses the web server, so both fields should point to port 8082:
Serverpod runs three servers locally (see `config/development.yaml`): the API server on port 8080, the Insights server on 8081, and the **web server on port 8082**.

| Environment | Authorized JavaScript origins | Authorized redirect URIs |
| --- | --- | --- |
| Local development | `http://localhost:8082` | `http://localhost:8082` |
| Production | Your web server's public URL (e.g., `https://my-awesome-project.serverpod.space`) | Your web server's public URL |
For the default popup / iFrame web flow, the main requirement is that your app's browser origin is listed under **Authorized JavaScript origins**.

You can find these ports in your server's `config/development.yaml` under `webServer`.
If you opt into redirect mode, you must also register the full callback URL under **Authorized redirect URIs**.

| Environment | Authorized JavaScript origins | Authorized redirect URIs |
| --- | --- | --- |
| Local development | Your Flutter web app origin, for example `http://localhost:3000` or `http://localhost:8082` | `http://localhost:8082/auth.html` if you use redirect mode |
| Production | Your deployed web app origin, for example `https://your-domain.com` | `https://your-domain.com/auth.html` if you use redirect mode |

In redirect mode, the redirect URI should point to an `auth.html` callback page. The browser is redirected there after sign-in, and that page sends the OAuth result back to your Flutter app.

You can find the Serverpod web server port in your server's `config/development.yaml` under `webServer`.

![Clients configuration](/img/authentication/providers/google/5-clients.png)

Expand All @@ -103,6 +116,8 @@ development:

Replace `your-client-id` and `your-client-secret` with the values from the Google Auth Platform. The `redirect_uris` must match the **Authorized redirect URIs** you configured in the previous step.

If you use redirect mode on web, the `redirect_uris` entry should point to your callback page, for example `http://localhost:8082/auth.html` locally or `https://your-domain.com/auth.html` in production.

For production, add the same `googleClientSecret` entry to the `production:` section of `passwords.yaml` (with your production redirect URI), or set the `SERVERPOD_PASSWORD_googleClientSecret` environment variable on your production server.

:::warning
Expand Down Expand Up @@ -169,7 +184,9 @@ Skipping the migration will cause the server to crash at runtime when the Google

## Client-side configuration

The Android and iOS integrations use the [google_sign_in](https://pub.dev/packages/google_sign_in) package under the hood, so any documentation there should also apply to this setup.
The Android and iOS integrations use the [google_sign_in](https://pub.dev/packages/google_sign_in) package under the hood, so any documentation there should also apply to those platforms.

On web, the default setup also uses the Google web integration from `google_sign_in_web`. If you provide a `redirectUri` when initializing Google sign-in, Serverpod switches to the redirect-based OAuth2 PKCE flow implemented with [flutter_web_auth_2](https://pub.dev/packages/flutter_web_auth_2).

### iOS

Expand Down Expand Up @@ -248,49 +265,116 @@ The downloaded `google-services.json` may not include a web OAuth client entry,

### Web

Web uses the same server OAuth client you created earlier, so you don't need a separate client. However, for web, the sign-in request originates from the Flutter app running in the browser, not from the Serverpod web server. Google requires this origin to be listed as well.
Web uses the same server OAuth client you created earlier, so you don't need a separate client.

1. **Choose a fixed port for your Flutter web app.** Google OAuth requires exact origin matches, and Flutter picks a random port on each run by default. To keep things consistent, run Flutter on a fixed port using `--web-port`:
You have two setup options on web.

```bash
flutter run -d chrome --web-hostname localhost --web-port=49660
#### Option 1: Default popup / iFrame flow

This is the existing behavior and requires the least setup.

1. Make sure your Flutter web app origin is listed under **Authorized JavaScript origins** on the Web application OAuth client.

2. Initialize Google sign-in in your Flutter app:

```dart
await client.auth.initialize();

await client.auth.initializeGoogleSignIn(
clientId: 'your-web-client-id.apps.googleusercontent.com',
);
```

3. Add `GoogleSignInWidget` to your UI. On web, this shows the Google-hosted web button in the default flow.

#### Option 2: Redirect mode with OAuth2 PKCE

Use this if you want the alternative redirect-based flow.

1. **Create the OAuth callback page.** In your Flutter project's `web/` folder, create a file named `auth.html` with the following content:

```html
<!DOCTYPE html>
<title>Authentication complete</title>
<p>Authentication is complete. If this does not happen automatically, please close the window.</p>
<script>
function postAuthenticationMessage() {
const message = {
'flutter-web-auth-2': window.location.href
};

if (window.opener) {
window.opener.postMessage(message, window.location.origin);
window.close();
} else if (window.parent && window.parent !== window) {
window.parent.postMessage(message, window.location.origin);
} else {
localStorage.setItem('flutter-web-auth-2', window.location.href);
window.close();
}
}

postAuthenticationMessage();
</script>
```

- `-d chrome`: Run on the Chrome browser.
- `--web-hostname localhost`: Bind to localhost.
- `--web-port=49660`: Use a fixed port (pick any available port). This is the value you will add to **Authorized JavaScript origins** in the next step.
2. **Update the Web application OAuth client.** Go back to the Web application client you created in the [previous section](#create-the-server-oauth-client-web-application) and make sure the following are configured:

2. **Update the server OAuth client.** Go back to the server OAuth client you created in the [previous section](#create-the-server-oauth-client-web-application) and add your Flutter web app's origin to **Authorized JavaScript origins**:
- **Authorized redirect URIs** includes the full URL to your callback page.
- **Authorized JavaScript origins** includes the browser origin where your Flutter web app runs.

- For local development: `http://localhost:49660` (or whichever port you chose)
- For production: your Flutter web app's domain (e.g., `https://my-awesome-project.serverpod.space`)
Example values:

The **Authorized redirect URIs** should already contain your Serverpod web server's address (`http://localhost:8082`) from the earlier setup. You don't need to change it.
| Environment | Authorized JavaScript origins | Authorized redirect URIs |
| --- | --- | --- |
| Local development | `http://localhost:8082` or your Flutter web origin if different | `http://localhost:8082/auth.html` |
| Production | `https://your-domain.com` | `https://your-domain.com/auth.html` |

![Web credentials configuration](/img/authentication/providers/google/2-credentials.png)

3. **Add the client ID to your Flutter project's `web/index.html`** (e.g., `my_project_flutter/web/index.html`). In the `<head>` section, add:
3. **Initialize Google sign-in with a redirect URI.** In your app startup code, pass the Web application client ID and the same redirect URI you registered in Google Cloud.

```html
<head>
...
<meta name="google-signin-client_id" content="your_server_client_id">
</head>
```dart
await client.auth.initialize();

await client.auth.initializeGoogleSignIn(
clientId: 'your-web-client-id.apps.googleusercontent.com',
redirectUri: 'http://localhost:8082/auth.html',
);
```

Replace `your_server_client_id` with the client ID from your Web application OAuth client.
For production, replace the redirect URI with your deployed `auth.html` URL.

:::note
You only need one `auth.html` file in your Flutter web project. It can be reused by multiple identity providers that rely on the same OAuth2 web callback mechanism.
:::

## Present the authentication UI

### Initialize the Google sign-in service

In your Flutter app's `main.dart` file (e.g., `my_project_flutter/lib/main.dart`), the template already sets up the `Client` and calls `client.auth.initialize()`. Add `client.auth.initializeGoogleSignIn()` right after it:
In your Flutter app's `main.dart` file (e.g., `my_project_flutter/lib/main.dart`), the template already sets up the `Client` and calls `client.auth.initialize()`.

For iOS, Android, and the default web popup / iFrame flow, add `client.auth.initializeGoogleSignIn()` right after it:

```dart
client.auth.initialize();
client.auth.initializeGoogleSignIn();
```

If you want the optional redirect-based web flow, pass `redirectUri` when initializing:

```dart
await client.auth.initialize();

await client.auth.initializeGoogleSignIn(
clientId: 'your-web-client-id.apps.googleusercontent.com',
redirectUri: 'http://localhost:8082/auth.html',
);
```

If your app targets multiple platforms, you can keep using the same `initializeGoogleSignIn(...)` API and only add `redirectUri` for the web redirect mode.

### Add the sign-in widget

If you have configured the `SignInWidget` as described in the [setup section](../../setup#present-the-authentication-ui), the Google identity provider will be automatically detected and displayed in the sign-in widget.
Expand Down Expand Up @@ -324,9 +408,9 @@ This renders a Google sign-in button like this:
The widget automatically handles:

- Google Sign-In flow for iOS, Android, and Web.
- Lightweight sign-in (One Tap, FedCM) support.
- Lightweight sign-in support on platforms where the underlying Google Sign-In package provides it.
- Token management.
- Underlying Google Sign-In package error handling.
- Underlying platform-specific authentication flow handling.

For details on how to customize the Google Sign-In UI in your Flutter app, see the [customizing the UI section](./customizing-the-ui).

Expand All @@ -340,10 +424,10 @@ Before going live, complete the following steps:

### 1. Update the OAuth redirect URIs

Go back to the [server OAuth client](#create-the-server-oauth-client-web-application) in the Google Auth Platform and add your production server's public URL to both **Authorized JavaScript origins** and **Authorized redirect URIs**:
Go back to the [server OAuth client](#create-the-server-oauth-client-web-application) in the Google Auth Platform and update the production values you actually use:

- **Authorized JavaScript origins**: `https://your-domain.serverpod.space`
- **Authorized redirect URIs**: `https://your-domain.serverpod.space`
- **Authorized JavaScript origins**: `https://your-domain.com`
- **Authorized redirect URIs**: `https://your-domain.com/auth.html` if you use redirect mode on web

Replace the URL with your actual production web server address.

Expand All @@ -359,11 +443,13 @@ production:
"web": {
"client_id": "your-client-id.apps.googleusercontent.com",
"client_secret": "your-client-secret",
"redirect_uris": ["https://your-domain.serverpod.space"]
"redirect_uris": ["https://your-domain.com/auth.html"]
}
}
```

If you only use the default popup / iFrame web flow, the `redirect_uris` entry is still part of the Google client secret JSON, but the browser flow does not depend on `auth.html`.

Alternatively, set the `SERVERPOD_PASSWORD_googleClientSecret` [environment variable](../../../07-configuration.md#2-via-environment-variables) on your production server with the same JSON value.

If you're deploying to Serverpod Cloud, set the password with the `scloud` CLI instead. Save the JSON to a file and run:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ If lightweight sign-in fails (e.g., no previous session exists or the user dismi

:::note
The lightweight sign-in attempt happens automatically when the controller is initialized, typically at app launch. If successful, users will be signed in without any additional interaction.

On web, lightweight sign-in behavior depends on which mode you use. The default web popup / iFrame flow continues to rely on `google_sign_in_web`, while the redirect-based OAuth2 flow is opt-in and does not use lightweight sign-in as its primary interaction model.
:::

### Configuring Client IDs on the App
Expand All @@ -188,6 +190,19 @@ client.auth.initializeGoogleSignIn(

This approach is useful when you need different client IDs per platform and want to manage them in your Dart code.

To opt into the redirect-based web flow, pass `redirectUri` to the same initializer:

```dart
await client.auth.initializeGoogleSignIn(
clientId: '<web_client_id>.apps.googleusercontent.com',
redirectUri: 'https://your-domain.com/auth.html',
);
```

If you omit `redirectUri` on web, Serverpod keeps using the default popup / iFrame flow.

Use the same Web application client ID and redirect URI you configured in Google Cloud.

#### Using Environment Variables

Alternatively, you can pass client IDs during build time using the `--dart-define` option. The Google Sign-In provider supports the following environment variables:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ GoogleSignInWidget(
'https://www.googleapis.com/auth/userinfo.profile',
],

// Whether to attempt lightweight sign-in (One Tap, FedCM)
// Whether to attempt lightweight sign-in on supported platforms
attemptLightweightSignIn: false,

onAuthenticated: () {
Expand Down Expand Up @@ -87,8 +87,13 @@ final controller = GoogleAuthController(
await controller.signIn();
```

:::warning
When using Google Sign-In on web, be mindful that the button will be rendered by the underlying `google_sign_in` package, so customizing the button might not work as expected. The included `GoogleSignInWidget` is a wrapper around the original widgets that already applies some customizations to make its design compatible between all platforms.
:::note
On web, the rendered button depends on how you initialize sign-in:

- If you call `initializeGoogleSignIn()` without `redirectUri`, the widget uses the Google-hosted web button from the default popup / iFrame flow.
- If you call `initializeGoogleSignIn(..., redirectUri: ...)`, the widget switches to the Flutter-rendered button and starts the redirect-based OAuth2 flow.

This means button customization on web is most consistent when you opt into redirect mode.
:::

### GoogleAuthController State Management
Expand Down
Loading
Loading